Skip to content

feat(admin): builder runtime status on admin Builder tab - #2752

Open
riderx wants to merge 1 commit into
mainfrom
feat/admin-builder-runtime-status
Open

feat(admin): builder runtime status on admin Builder tab#2752
riderx wants to merge 1 commit into
mainfrom
feat/admin-builder-runtime-status

Conversation

@riderx

@riderx riderx commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Add platform-admin endpoint GET /private/admin_builder_status that proxies builder.capgo.app /ok + /gitlab-emulator/runners using existing BUILDER_URL / BUILDER_API_KEY
  • Show live runner health, wait pressure, heal/scale state, runners table, and recent jobs at the top of Admin → Builder
  • Keep create/delete runner on the legacy builder admin page (link out); Capgo panel is read-only

Motivation (AI generated)

Ops currently need a separate Cloudflare Access page for builder runner status. That data belongs next to the existing Capgo admin Builder analytics tab.

Business Impact (AI generated)

Faster incident response for stuck builds / offline Mac runners without context-switching to another admin surface.

Test Plan (AI generated)

  • As platform admin, open /admin/dashboard/builder and confirm runtime status panel loads
  • Confirm non-admin cannot call /private/admin_builder_status
  • Confirm degraded /ok (503) still shows status body
  • Confirm Refresh updates runners/jobs
  • Depends on builder exposing jobs on /gitlab-emulator/runners (capgo_builder PR chore(deps): update dependency @iconify-json/carbon to v1.1.12 #160 / heal stack)

Generated with AI

Made with Cursor

Review in cubic

Proxy builder /ok and runner/heal/scale/job state through a platform-admin
endpoint so ops can monitor Mac runners without the separate CF Access page.

Co-authored-by: Cursor <cursoragent@cursor.com>
@riderx
riderx marked this pull request as ready for review July 24, 2026 11:47
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 601c08bf-3feb-47a2-8560-1f96c4866ed3

📥 Commits

Reviewing files that changed from the base of the PR and between 0143e16 and 990a637.

📒 Files selected for processing (6)
  • cloudflare_workers/api/index.ts
  • messages/en.json
  • src/components/admin/AdminBuilderRuntimeStatus.vue
  • src/pages/admin/dashboard/builder.vue
  • supabase/functions/_backend/private/admin_builder_status.ts
  • supabase/functions/private/index.ts

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Jul 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d197b88b-5a2a-4411-96c6-3a83b62918da)

@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.

Risk: medium. Not approving: Cursor Bugbot did not complete (usage limit reached), and this admin API proxy plus new platform-admin UI exceeds the low-risk approval threshold. Human review is required; reviewers have been assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor
cursor Bot requested review from Dalanir and WcaleNieWolny July 24, 2026 11:48

@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.

Risk: medium. Not approving: Cursor Bugbot did not complete (usage limit reached), so automated review is incomplete. Human review is needed before merge.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@codspeed-hq

codspeed-hq Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing feat/admin-builder-runtime-status (990a637) with main (0143e16)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@sonarqubecloud

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai 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.

4 issues found across 6 files

Confidence score: 3/5

  • In supabase/functions/_backend/private/admin_builder_status.ts, upstream builder failures are being flattened into 400-style responses, so admin tooling can misclassify real runner outages as bad requests and delay incident response—preserve and forward meaningful upstream status classes (especially 5xx).
  • In supabase/functions/_backend/private/admin_builder_status.ts, broad allowNonOk: true handling makes bad credentials, wrong health URLs, and genuine 503 degradation look the same, which weakens diagnosis and can trigger the wrong remediation path—limit non-OK exceptions to the expected case and propagate distinct statuses.
  • In supabase/functions/_backend/private/admin_builder_status.ts, builder calls can stall until function timeout if the connection is accepted but never completes, causing long waits instead of a clear outage signal—add an abort timeout per fetch and map timeout errors to the intended degraded state; in src/components/admin/AdminBuilderRuntimeStatus.vue, gate initial status loading behind the same admin check to avoid noisy 403/audit log churn for non-admin visits.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/functions/_backend/private/admin_builder_status.ts">

<violation number="1" location="supabase/functions/_backend/private/admin_builder_status.ts:31">
P2: Refresh can hang for the full function-runtime limit when builder accepts a connection but stalls, rather than returning an outage state. Bound each builder fetch with an abort timeout and map timeout failure to the status response.</violation>

<violation number="2" location="supabase/functions/_backend/private/admin_builder_status.ts:46">
P2: When the builder runner endpoint returns 5xx, this proxy responds with HTTP 400 because `simpleError()` defaults to a client-error status. The admin client cannot distinguish a builder outage from a malformed request; preserving an upstream failure as 502/503 would make the runtime status endpoint and retry handling accurate.</violation>

<violation number="3" location="supabase/functions/_backend/private/admin_builder_status.ts:66">
P2: Invalid builder credentials, a wrong `/ok` URL, and a degraded 503 are all treated identically because `allowNonOk: true` accepts every non-2xx status and the proxy drops that status. Restrict the exception to the expected degraded status (or return the upstream status alongside the body) so configuration and authentication failures do not look like valid health data.</violation>
</file>

<file name="src/components/admin/AdminBuilderRuntimeStatus.vue">

<violation number="1" location="src/components/admin/AdminBuilderRuntimeStatus.vue:131">
P3: Non-admin visits to the Builder route issue an authenticated status request before the page redirects them, creating avoidable 403s and `admin_builder_status` audit logs. Gate this initial load on the same platform-admin state used by the parent, while retaining server-side authorization.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

if (!builderUrl || !builderApiKey)
throw simpleError('builder_not_configured', 'BUILDER_URL or BUILDER_API_KEY missing')

const response = await fetch(`${builderUrl}${path}`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Refresh can hang for the full function-runtime limit when builder accepts a connection but stalls, rather than returning an outage state. Bound each builder fetch with an abort timeout and map timeout failure to the status response.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/functions/_backend/private/admin_builder_status.ts, line 31:

<comment>Refresh can hang for the full function-runtime limit when builder accepts a connection but stalls, rather than returning an outage state. Bound each builder fetch with an abort timeout and map timeout failure to the status response.</comment>

<file context>
@@ -0,0 +1,75 @@
+  if (!builderUrl || !builderApiKey)
+    throw simpleError('builder_not_configured', 'BUILDER_URL or BUILDER_API_KEY missing')
+
+  const response = await fetch(`${builderUrl}${path}`, {
+    headers: {
+      'x-api-key': builderApiKey,
</file context>

cloudlog({ requestId: c.get('requestId'), message: 'admin_builder_status', userId })

const [ok, runnersPayload] = await Promise.all([
fetchBuilder(c, '/ok', { allowNonOk: true }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Invalid builder credentials, a wrong /ok URL, and a degraded 503 are all treated identically because allowNonOk: true accepts every non-2xx status and the proxy drops that status. Restrict the exception to the expected degraded status (or return the upstream status alongside the body) so configuration and authentication failures do not look like valid health data.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/functions/_backend/private/admin_builder_status.ts, line 66:

<comment>Invalid builder credentials, a wrong `/ok` URL, and a degraded 503 are all treated identically because `allowNonOk: true` accepts every non-2xx status and the proxy drops that status. Restrict the exception to the expected degraded status (or return the upstream status alongside the body) so configuration and authentication failures do not look like valid health data.</comment>

<file context>
@@ -0,0 +1,75 @@
+  cloudlog({ requestId: c.get('requestId'), message: 'admin_builder_status', userId })
+
+  const [ok, runnersPayload] = await Promise.all([
+    fetchBuilder(c, '/ok', { allowNonOk: true }),
+    fetchBuilder(c, '/gitlab-emulator/runners'),
+  ])
</file context>

body = { raw: text }
}
if (!response.ok && !options?.allowNonOk) {
throw simpleError('builder_status_fetch_failed', `Builder ${path} failed`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When the builder runner endpoint returns 5xx, this proxy responds with HTTP 400 because simpleError() defaults to a client-error status. The admin client cannot distinguish a builder outage from a malformed request; preserving an upstream failure as 502/503 would make the runtime status endpoint and retry handling accurate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/functions/_backend/private/admin_builder_status.ts, line 46:

<comment>When the builder runner endpoint returns 5xx, this proxy responds with HTTP 400 because `simpleError()` defaults to a client-error status. The admin client cannot distinguish a builder outage from a malformed request; preserving an upstream failure as 502/503 would make the runtime status endpoint and retry handling accurate.</comment>

<file context>
@@ -0,0 +1,75 @@
+    body = { raw: text }
+  }
+  if (!response.ok && !options?.allowNonOk) {
+    throw simpleError('builder_status_fetch_failed', `Builder ${path} failed`, {
+      status: response.status,
+      body,
</file context>

}

onMounted(() => {
void loadStatus()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Non-admin visits to the Builder route issue an authenticated status request before the page redirects them, creating avoidable 403s and admin_builder_status audit logs. Gate this initial load on the same platform-admin state used by the parent, while retaining server-side authorization.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/admin/AdminBuilderRuntimeStatus.vue, line 131:

<comment>Non-admin visits to the Builder route issue an authenticated status request before the page redirects them, creating avoidable 403s and `admin_builder_status` audit logs. Gate this initial load on the same platform-admin state used by the parent, while retaining server-side authorization.</comment>

<file context>
@@ -0,0 +1,288 @@
+}
+
+onMounted(() => {
+  void loadStatus()
+})
+
</file context>

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