CI control room for watching and unsticking Buildkite builds

By General Input

One screen for every build in flight, the logs behind the failures, and the retry, cancel, and unblock buttons your platform team reaches for all day.

Integrations

  • Buildkite
  • Slack Bot

Type

App

Categories

  • Engineering
  • Operations

I want a CI control room app that my platform team keeps open all day to watch and unstick builds without living in the Buildkite web interface. Build it as an app backed by Buildkite for everything about builds and agents, and Slack Bot for handing a build off to a teammate. This is a hands-on console, not an alerting workflow: nothing runs on a schedule, a person sits in front of it and presses buttons.

Resolve the Buildkite organization first with List Organizations, because almost every other path is scoped by organization slug. Then use List Pipelines to populate a settings view where someone picks which pipelines the board watches, how far back recent builds go (default the last 24 hours), and how often the board refreshes itself. Persist that configuration for the whole app so the team opens the same board. Keep the refresh interval sensible, around 20 to 30 seconds, because Buildkite rate limits per organization over a rolling 60 second window and this app sits open all day; back off politely when a request comes back rate limited rather than hammering, and always offer a manual refresh control too.

The main tab is a live board of in-flight and recent builds, grouped under their pipeline, loaded with List Builds for an Organization and filtered to the watched pipelines. Sort so anything still running or scheduled floats to the top, then finished builds newest first. Each row shows the build state as a coloured badge, the branch, who kicked it off, and elapsed time. For who kicked it off, use the build creator when there is one and fall back to the commit author, since builds started by a schedule or by the API often have no creator. For elapsed time, count from the start time for a running build and show total duration for a finished one; a build that is scheduled but has not started yet should read as queued with the time since it was created. Put a failed-only toggle in the header that narrows the board to failed and cancelled builds.

Flag builds that are running noticeably longer than normal for their pipeline. Work out a baseline per pipeline in the handler by taking recent finished builds that passed and computing the median of their durations, then flag any in-flight build whose elapsed time exceeds that baseline by a configurable multiplier, defaulting to 1.5 times. Show the flag as a clear visual marker on the row with the comparison spelled out, something like "running 22m, usually 9m". When a pipeline has too little history to have a meaningful median, skip the flag for it rather than guessing. Make the multiplier editable in settings so a noisy pipeline can be tuned down.

Clicking a row opens a build detail panel. Important: Buildkite builds are addressed by their per-pipeline sequential number, not by their UUID, so the detail route has to carry both the pipeline slug and the build number. Load the full record with Get a Build, which includes the per-job breakdown. Identify the failing job or jobs from that breakdown and pull the log with Get a Job's Log Output, but render only the tail, roughly the last 200 lines, in a monospaced scrolling block; build logs get enormous and the whole thing should never be loaded into the page. Alongside the log, show the build's annotations using List Annotations for a Build, rendering each one according to its style so errors and warnings read differently from information.

The detail panel is where the work happens, so wire up the action buttons. Retry Failed Jobs in a Build retries everything that failed in one press. Retry a Job sits on each individual job row for retrying just that step. Cancel a Build appears on builds still running or scheduled. Rebuild a Build appears on finished builds. Unblock a Job appears on any job that is a block step sitting waiting, so approvals can be released without switching tabs. Optionally add Reprioritize a Job for bumping a job up the queue when something urgent is stuck behind a backlog. Ask for confirmation before cancelling, refetch that build right after any action so the panel reflects the new state, and record which signed-in user pressed which button so the team can see who did what.

A second tab shows the agent fleet from List Agents. Give each agent a row with its name and hostname, connection state, the job it is currently running (linked through to that job's build when there is one), the queue it serves, and its agent version. Let the list be filtered by queue and by connection state, since finding the one wedged agent among many is the whole point. Put Pause an Agent and Stop an Agent buttons on each row, and Resume an Agent on any agent that is already paused, because pausing without a way back is a trap. Explain the difference inline: pausing stops an agent taking new jobs while it finishes its current one, which is what draining looks like, while stopping ends it outright. Confirm before stopping.

Add a share button on every build, both on the board row and inside the detail panel. It opens a dialog with a Slack channel picker populated by List Channels and an editable preview of the message, then posts with Send a Message. The message should be a formatted summary rather than a link dump: pipeline name, build number, state, branch, who triggered it, how long it ran, the name of the failing job, a short excerpt from the end of that job's log, and the Buildkite link to the build taken from the build's own web URL. Remember the last channel used and offer it as the default next time, and let the sender add a line of their own before posting, since handing off usually comes with context.

On permissions: viewing the board needs only read access to builds, pipelines, agents, and build logs, while every action button needs a token with write access. When the connected token cannot write, keep the action buttons visible but disabled with a short inline explanation rather than hiding them, so nobody wonders where the buttons went. One thing to avoid: do not build artifact downloading into this app, because those download links are short-lived redirects that expire in about a minute and are not worth storing or reusing.

Related prompts

Explore more prompts
A brand asset library your marketing team actually searchesTurn Mailjet email clicks into ranked HubSpot follow-upsClean out the Looker dashboards and Looks nobody opensLiveKit live operations console for room moderationWake up dormant Keap leads with a researched reasonLiveChat coverage board for planning next week's shiftsPhone routing control panel for LiveKit voice agentsLinkedIn Ads budget pacing dashboard for every client accountGive your team Looker numbers without buying more seatsPause marketing emails to escalated customers, then restore them