Skip to content

Live PR status badge on Kanban cards (web parity + daemon refresh)#619

Merged
bborn merged 1 commit into
mainfrom
task/4468-live-pr-status-badge-on-kanban-cards
Jun 23, 2026
Merged

Live PR status badge on Kanban cards (web parity + daemon refresh)#619
bborn merged 1 commit into
mainfrom
task/4468-live-pr-status-badge-on-kanban-cards

Conversation

@bborn

@bborn bborn commented Jun 23, 2026

Copy link
Copy Markdown
Owner

What & why

The TUI kanban already renders a live PR badge (state + checks + diff stats) and refreshes PR info on a tick — but the web/desktop board only showed a bare purple PR #N chip and never surfaced state or CI, and the cached PR state only updated when a TUI was open. This brings the web board to parity and keeps the cache fresh daemon-side so the badge updates without a restart.

Changes

Web API (internal/web/handlers.go, board.go)

  • taskJSON and BoardEntry now carry a pr object — state (open/draft/merged/closed), check_state (passing/failing/pending/""), mergeable, additions, deletions — decoded from the cached PRInfoJSON already persisted on each task. Omitted when there's no PR.

Web UI (desktop/src/components/Board.tsx, api/types.ts)

  • New PRBadge: state-colored icon (open=green, draft=gray, merged=purple GitMerge, closed=red), a CI check mark (passing / failing / pending; merge conflicts read as failing, matching the TUI), and +/- diff stats. Falls back to a bare chip for legacy rows with a URL but no cached state yet. Card memo equality compares the new PR fields so SSE ticks repaint on change.

Live updates (internal/db/tasks.go)

  • UpdateTaskPRInfo records a board-change event only when the PR JSON actually changes, so the SSE board stream re-pushes live without spamming the feed on idle refreshes.

Daemon refresh (internal/executor/executor.go)

  • refreshActivePRInfo refreshes cached PR state for processing/blocked tasks with a branch on a 90s tick, reusing the rate-limit-gated batch FetchAllPRsForRepo. It carries forward a previously-known CI rollup (the batch path omits statusCheckRollup by design) so checks aren't wiped, and skips terminal PRs (left to reconcileReviewTasks). Keeps the web board live even with no TUI open.

Render-cache gotcha — verified

hashTaskCard already hashes every PR field the TUI badge reads (State, CheckState, Mergeable, Additions, Deletions). No new TUI card field was added, so the existing render signature covers the badge — no stale-card regression.

Tests

  • internal/web/pr_badge_test.go: PR payload present/omitted on /api/tasks, BuildBoardSnapshot includes PR, state/check string mappings.
  • internal/db: UpdateTaskPRInfo emits a board event on change and stays quiet on a no-op.
  • go test green for db/web/executor/github/ui; gofmt clean; golangci-lint v2.8.0 clean; tsc --noEmit clean.

Acceptance

  • A task with a PR shows state + checks on the card, TUI and web, updating without a restart.
  • Render-cache signature already covers PR fields; no stale-card regression.
  • Periodic refresh for in-progress/blocked tasks with a branch (TUI tick + new daemon tick), rate-limit respected.

🤖 Generated with Claude Code

The TUI kanban already renders a live PR badge (state + checks + diff) and
refreshes PR info on a tick, but the web/desktop board only showed a bare PR
chip and never surfaced state or CI. This brings the web to parity and keeps
the cached PR state fresh daemon-side so the badge updates without a restart.

- web API: taskJSON / BoardEntry now carry a `pr` object (state, check_state,
  mergeable, additions, deletions) decoded from the cached PRInfoJSON.
- web UI (Board.tsx): PRBadge renders state-colored icon (open/draft/merged/
  closed), a CI check mark (passing/failing/pending, conflicts as failing), and
  diff stats; falls back to a bare chip for legacy rows without cached state.
- db: UpdateTaskPRInfo records a board-change event only when the PR JSON
  actually changes, so the SSE feed re-pushes the board live without spamming.
- executor: refreshActivePRInfo refreshes cached PR state for processing/blocked
  tasks with a branch on a 90s tick (batch list, rate-limit gated), carrying
  forward a known CI rollup so checks aren't wiped.

Render cache: hashTaskCard already hashes every PR field the TUI badge reads, so
the existing signature covers it — no stale-card regression.

Tests: web PR payload (present/omitted, board snapshot, string mappings), db
event-on-change/no-op, all touched Go packages + tsc green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bborn

bborn commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

QA — web board PR badge (live)

Built the UI into ty (make build-ui + go build -tags ui), seeded a throwaway DB (WORKTREE_DB_PATH) with tasks across every PR state, ran ty serve, and screenshotted the real served React app (not an isolated component render). Images re-hosted to a public bucket and verified HTTP 200.

Full board — every state on real app chrome:
web-board

Close-up — icons + colors + diff stats:
web-board-closeup

States verified on the cards:

/api/tasks payload confirmed to carry the pr object (state + check_state + mergeable + additions/deletions) for each, and to omit it for the two backlog tasks with no PR. The badge repaints live as state changes (DB emits a board event only on real PR-JSON change → SSE re-push).

@bborn bborn marked this pull request as ready for review June 23, 2026 22:57
@bborn bborn merged commit 19c5503 into main Jun 23, 2026
4 checks passed
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.

1 participant