Skip to content

feat(checks): make the Jenkins build tree readable at scale - #40

Merged
nplez1 merged 2 commits into
nplez1/mainfrom
nplez1/Jenkins-Details
Sep 29, 2026
Merged

nplez1 merged 2 commits into
nplez1/mainfrom
nplez1/Jenkins-Details

Conversation

@nplez1

@nplez1 nplez1 commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

ELI5

Orca can now read a Jenkins build and paint its pipeline. But the first pass was too much: the
Checks pane listed every stage of a pipeline that can have hundreds, the failing stage was buried
underneath, an "Output" box only repeated the build's name, and a Jenkins permissions problem was a
dead-end sentence. This makes the pane short, draws the pipeline as a tree like Blue Ocean's, puts
the failures on top, and turns a fixable Jenkins error into a button that opens Settings.

What Changed

Before: expanding a Jenkins check listed every stage of the build — hundreds of rows for a
large pipeline — with the failed and still-running stages mixed in. The check-details tab repeated
the full stage list with no nesting, opened onto an Output box whose only content was the build's
display name, and reported a permissions problem with a sentence you could not act on. A build's
failure text was not read at all.

After:

  • The pane lists only the stages still running and failed, running first, capped at eight,
    with a +N more stages line. A passing stage is no longer noise.
  • The check-details tab renders the stages as a nested tree (stage → its steps) with a glyph and
    duration per node; the failed and running stages are open by default.
  • A Failures block sits directly under the run header naming each failed stage and its error
    text, so "what broke" needs no scrolling.
  • The Output box is gone for a stage list; the build metadata is the run summary.
  • A Jenkins failure the user can fix — no server configured, a rejected token, or no permission —
    now shows an Open Jenkins settings button that deep-links to the Jenkins card in
    Settings → Integrations.

The mechanism. The pane reduction is paneCheckSteps in src/shared/check-step-visibility.ts.
The tree and failure text come from one more bounded round of reads: main follows each stage's own
wfapi/describe (_links.self.href, resolved against the build and refused unless it stays under
the configured server), bounded to 40 requests at six concurrent, failed/running first, under a
four-second budget
. The mapper nests the stageFlowNodes on an optional PRCheckStep.children
and lifts the failing node's error.message onto the stage, so the flat pane list and the nested
tree read the same data. The Jenkins node id is carried so repeated stage names cannot collide as
React keys. The settings affordance is a typed JenkinsCheckDetailsError whose reason becomes a
serializable errorAction on the pane and tab state — not a callback, because that state is
persisted — cleared on every success and retry.

Why

A cap with priority, not an unbounded fan-out. A pipeline can have hundreds of stages, and the
Blue Ocean tree needs each stage's own data. wfapi exposes that one stage at a time, so a fan-out
is unavoidable; fetching failed and running stages first means the cap can only ever hide the steps
of later passing stages, never the failure that brought the user here. The four-second budget is the
real latency guard, so the request cap is only a server-load ceiling.

One nested, provider-neutral field rather than a Jenkins-only tree type. children and
errorMessage are generic enough to cross IPC, and threading a second tree through
PRCheckRunDetails and every surface would be more churn. The flat consumers explicitly do not
recurse — that is documented on the type and covered by a test — so outcomes are never double-counted.

The node id as the key. Loops and parallel branches repeat stage names, and parallel branches can
share a start time or have none; a name + startedAt key drops or duplicates rows. The id is
Jenkins' own and unique per run.

A structured error action, not message matching. failureMessage localizes at throw time, so
the reason is gone by the time a surface decides whether to offer a link. The typed error carries it
forward, and only the fixable reasons (not-configured, unauthorized, forbidden) offer the button.

Alternatives considered: depending on the Blue Ocean REST /nodes endpoint for a one-shot tree
(rejected — it needs the Blue Ocean plugin, which we cannot assume on psci-jenkins.adobe.io, and a
missing plugin would silently empty the pane); removing the Output box for every provider (rejected —
it is real check output for GitHub and GitLab, so it is dropped only for a stage list).

Linked Issue

None filed — nothing to link (same as #39). Say the word if you want one opened and referenced here.

Visual Proof

No screenshots: this needs a live Jenkins plus a running app, and capturing one here would be
dishonest. What follows is the rendered text of the components, whitespace-normalized, taken
from the component tests (jenkins-check-row-details.test.tsx,
CheckRunDetailsPanel.jenkins.test.tsx) — real renders, not hand-written.

Before — the pane's stage list was every stage, including the passing ones:

Failed jobs
  #12          failure
    ✓ Checkout            success
    ✗ Unit tests          failure
    ◌ Integration tests   pending
    – Deploy              skipped

After — only what is running and what failed, running first:

Failed jobs
  #12
    ◌ Integration tests   pending
    ✗ Unit tests          failure

After — the check-details tab, top down:

continuous-integration/jenkins/pr-merge              [Fix with AI] [Refresh]
Status: Failed   Started Jun 16, 5:00 AM   Completed Jun 16, 5:04 AM   Duration 4m 12s

Failures  1
  Test
    script returned exit code 1

Build
  Number        #12
  Triggered by  Started by user Ada
  Queued        2s
  Estimated     4m 0s
Parameters
  GITHUB_TOKEN  ••••
Commits
  abc123de      Fix the thing          Ada Lovelace

Failed jobs                                             1 / 2 steps failed
  #12
    ▾ ✗ Test                          56s
        ✗ Shell Script                56s
          script returned exit code 1
    ▸ ✓ Checkout                      4s

Testing

  • pnpm tc (all projects) clean.

  • pnpm run check:code-quality:changed clean (all seven scans, including design-system and React
    Doctor).

  • Localization extraction / catalogs / coverage green; check:max-lines-ratchet green.

  • Affected suites: full src/shared, src/main/jenkins, and the whole editor, right-sidebar
    and store directories — green (≈17,000 tests across those runs).

  • New coverage worth naming: pane filtering/cap/order (6), rendered settings button and its clearing
    after a retry (2), the stage tree including a 250-stage list with 50 repeated names and no key
    collision
    (2), the failure summary (2), Output-box removal for a stage list (1), settings-error
    typing (2), reducer identity for a repeated patch (1), a stage detail that never answers ends at
    the budget
    (1), and the 40-request cap (1).

  • Platforms: macOS only. No live Jenkins was exercised; every endpoint interaction is mocked.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Implemented by Pi (coding agent), with an independent review pass from the advisor escalation
before finalizing. That review found three real issues, all fixed: React key collisions for repeated
stage names (now keyed on the Jenkins node id), a regression where a check whose only output was a
title lost its Output box (restored for non-stage providers), and an inaccurate comment claiming the
request cap could never hide failure text.

Review

Worth a reviewer's eye:

  1. The 40-stage cap. Stages past it render as rows with no steps. Failed and running stages are
    fetched first, so the failure path is unaffected; a very large pipeline will show every stage name
    but only expand the failed/running ones plus the earliest passing ones. Raising the cap is a
    one-constant change if we want fuller coverage (MAX_STAGE_DETAIL_REQUESTS).
  2. The flat consumers must not recurse. visibleCheckSteps, paneCheckSteps and
    summarizeJobSteps read top-level steps only; only CheckStageTree descends into children.
    A test pins the pane's non-recursion.
  3. Where the anchor lives. The settings deep link scrolls to [data-settings-section= "integrations-jenkins"], which the Jenkins card now sets. The id is a lib constant shared by the
    card and both surfaces.
  4. The Output-box rule. hasOutput = (title || summary || text) && !showsStageList — the drop is
    scoped to a stage list, not to a provider name.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Also in this PR: a second commit splits five files that were over the oxlint max-lines budget
    on nplez1/main (pre-existing — pushed over by fork-only commits that never ran PR CI), which was
    failing the whole-repo lint gate for every PR cut from it. No behavior change; the repo's rule is
    to split, not suppress.
  • Security: a server-supplied stage href is followed only if it stays under the configured server
    prefix (or the build origin when unconfigured), so the Authorization header never travels to a
    host we did not configure.
  • Cross-platform: platform-neutral (URL handling plus the shared main-process HTTP port); only
    macOS was run.
  • SSH/remote: unchanged — details are read from the machine running Orca.
  • Mobile: no mobile surface consumes check details.
  • Backwards compatibility: PRCheckStep.id/children/errorMessage and the new errorAction
    are optional, so an older client ignores them.
  • Performance: the extra reads are bounded to 40 requests at six concurrent inside a four-second
    budget; the pane is capped at eight rows regardless of pipeline size.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)
    — locally: pnpm tc, the affected suites and every affected pnpm lint gate are green; the
    full pnpm test and pnpm build were left to CI.

The Jenkins details from #39 read the build's stage list but listed every stage in the Checks
pane, buried failures under it, kept an Output box that only repeated the build name, and
stopped at a bare error when the account could not read the job.

- Pane: list only the stages still running and the ones that failed, running first, capped at
  eight, with a "+N more stages" line — a pipeline can have hundreds.
- Detail page: render the stages as a nested tree (stage -> its wfapi flow nodes) like Blue
  Ocean, with failed and running stages open by default.
- Detail page: a Failures block under the run header names each failed stage and its error
  text, so "what broke" needs no scrolling.
- Detail page: drop the Output box for a stage list; the build metadata is the run summary.
- Settings: a Jenkins read failure the user can fix (no server, rejected token, no permission)
  now shows an "Open Jenkins settings" button that deep-links to the Jenkins card.

Main reads each stage's own wfapi/describe (its _links.self.href, bounded to 40 requests at
six concurrent, failed/running first under a four-second budget) for the steps and failure
text, and carries the flow nodes on an optional PRCheckStep.children. The Jenkins node id is
kept so repeated stage names (loops, parallel branches) cannot collide as React keys.
Five files exceed the oxlint max-lines budget on nplez1/main and have no suppression, so the
whole-repo `pnpm exec oxlint` in the static-analysis job fails for every PR cut from it. They
were pushed over by fork-only commits (`#34`, and three direct commits) that never ran PR CI.
The repo's rule is to split rather than suppress, so this extracts cohesive pieces:

- filesystem-watcher.test.ts: the local/WSL lifecycle tests move to
  filesystem-watcher-local-lifecycle.test.ts.
- filesystem-watcher-local-subscription.ts: scheduleLocalWatcherTeardown moves to
  filesystem-watcher-local-teardown.ts.
- ssh-target-cleanup.ts: the per-tab session helpers move to ssh-target-tab-sessions.ts.
- ai-vault-session-refresh.ts: the session-ids key moves to ai-vault-session-ids.ts.
- accounts-search.ts: the Grok and Cursor search entries move to
  accounts-search-extra-providers.ts and are re-exported.

No behavior change; the affected suites pass.
@nplez1
nplez1 merged commit bb02f27 into nplez1/main Sep 29, 2026
48 checks passed
nplez1 added a commit that referenced this pull request Sep 29, 2026
* feat(checks): make the Jenkins build tree readable at scale

The Jenkins details from #39 read the build's stage list but listed every stage in the Checks
pane, buried failures under it, kept an Output box that only repeated the build name, and
stopped at a bare error when the account could not read the job.

- Pane: list only the stages still running and the ones that failed, running first, capped at
  eight, with a "+N more stages" line — a pipeline can have hundreds.
- Detail page: render the stages as a nested tree (stage -> its wfapi flow nodes) like Blue
  Ocean, with failed and running stages open by default.
- Detail page: a Failures block under the run header names each failed stage and its error
  text, so "what broke" needs no scrolling.
- Detail page: drop the Output box for a stage list; the build metadata is the run summary.
- Settings: a Jenkins read failure the user can fix (no server, rejected token, no permission)
  now shows an "Open Jenkins settings" button that deep-links to the Jenkins card.

Main reads each stage's own wfapi/describe (its _links.self.href, bounded to 40 requests at
six concurrent, failed/running first under a four-second budget) for the steps and failure
text, and carries the flow nodes on an optional PRCheckStep.children. The Jenkins node id is
kept so repeated stage names (loops, parallel branches) cannot collide as React keys.

* fix(lint): split files over the max-lines budget

Five files exceed the oxlint max-lines budget on nplez1/main and have no suppression, so the
whole-repo `pnpm exec oxlint` in the static-analysis job fails for every PR cut from it. They
were pushed over by fork-only commits (`#34`, and three direct commits) that never ran PR CI.
The repo's rule is to split rather than suppress, so this extracts cohesive pieces:

- filesystem-watcher.test.ts: the local/WSL lifecycle tests move to
  filesystem-watcher-local-lifecycle.test.ts.
- filesystem-watcher-local-subscription.ts: scheduleLocalWatcherTeardown moves to
  filesystem-watcher-local-teardown.ts.
- ssh-target-cleanup.ts: the per-tab session helpers move to ssh-target-tab-sessions.ts.
- ai-vault-session-refresh.ts: the session-ids key moves to ai-vault-session-ids.ts.
- accounts-search.ts: the Grok and Cursor search entries move to
  accounts-search-extra-providers.ts and are re-exported.

No behavior change; the affected suites pass.
nplez1 added a commit that referenced this pull request Oct 1, 2026
* feat(checks): make the Jenkins build tree readable at scale

The Jenkins details from #39 read the build's stage list but listed every stage in the Checks
pane, buried failures under it, kept an Output box that only repeated the build name, and
stopped at a bare error when the account could not read the job.

- Pane: list only the stages still running and the ones that failed, running first, capped at
  eight, with a "+N more stages" line — a pipeline can have hundreds.
- Detail page: render the stages as a nested tree (stage -> its wfapi flow nodes) like Blue
  Ocean, with failed and running stages open by default.
- Detail page: a Failures block under the run header names each failed stage and its error
  text, so "what broke" needs no scrolling.
- Detail page: drop the Output box for a stage list; the build metadata is the run summary.
- Settings: a Jenkins read failure the user can fix (no server, rejected token, no permission)
  now shows an "Open Jenkins settings" button that deep-links to the Jenkins card.

Main reads each stage's own wfapi/describe (its _links.self.href, bounded to 40 requests at
six concurrent, failed/running first under a four-second budget) for the steps and failure
text, and carries the flow nodes on an optional PRCheckStep.children. The Jenkins node id is
kept so repeated stage names (loops, parallel branches) cannot collide as React keys.

* fix(lint): split files over the max-lines budget

Five files exceed the oxlint max-lines budget on nplez1/main and have no suppression, so the
whole-repo `pnpm exec oxlint` in the static-analysis job fails for every PR cut from it. They
were pushed over by fork-only commits (`#34`, and three direct commits) that never ran PR CI.
The repo's rule is to split rather than suppress, so this extracts cohesive pieces:

- filesystem-watcher.test.ts: the local/WSL lifecycle tests move to
  filesystem-watcher-local-lifecycle.test.ts.
- filesystem-watcher-local-subscription.ts: scheduleLocalWatcherTeardown moves to
  filesystem-watcher-local-teardown.ts.
- ssh-target-cleanup.ts: the per-tab session helpers move to ssh-target-tab-sessions.ts.
- ai-vault-session-refresh.ts: the session-ids key moves to ai-vault-session-ids.ts.
- accounts-search.ts: the Grok and Cursor search entries move to
  accounts-search-extra-providers.ts and are re-exported.

No behavior change; the affected suites pass.
nplez1 added a commit that referenced this pull request Oct 6, 2026
* feat(checks): make the Jenkins build tree readable at scale

The Jenkins details from #39 read the build's stage list but listed every stage in the Checks
pane, buried failures under it, kept an Output box that only repeated the build name, and
stopped at a bare error when the account could not read the job.

- Pane: list only the stages still running and the ones that failed, running first, capped at
  eight, with a "+N more stages" line — a pipeline can have hundreds.
- Detail page: render the stages as a nested tree (stage -> its wfapi flow nodes) like Blue
  Ocean, with failed and running stages open by default.
- Detail page: a Failures block under the run header names each failed stage and its error
  text, so "what broke" needs no scrolling.
- Detail page: drop the Output box for a stage list; the build metadata is the run summary.
- Settings: a Jenkins read failure the user can fix (no server, rejected token, no permission)
  now shows an "Open Jenkins settings" button that deep-links to the Jenkins card.

Main reads each stage's own wfapi/describe (its _links.self.href, bounded to 40 requests at
six concurrent, failed/running first under a four-second budget) for the steps and failure
text, and carries the flow nodes on an optional PRCheckStep.children. The Jenkins node id is
kept so repeated stage names (loops, parallel branches) cannot collide as React keys.

* fix(lint): split files over the max-lines budget

Five files exceed the oxlint max-lines budget on nplez1/main and have no suppression, so the
whole-repo `pnpm exec oxlint` in the static-analysis job fails for every PR cut from it. They
were pushed over by fork-only commits (`#34`, and three direct commits) that never ran PR CI.
The repo's rule is to split rather than suppress, so this extracts cohesive pieces:

- filesystem-watcher.test.ts: the local/WSL lifecycle tests move to
  filesystem-watcher-local-lifecycle.test.ts.
- filesystem-watcher-local-subscription.ts: scheduleLocalWatcherTeardown moves to
  filesystem-watcher-local-teardown.ts.
- ssh-target-cleanup.ts: the per-tab session helpers move to ssh-target-tab-sessions.ts.
- ai-vault-session-refresh.ts: the session-ids key moves to ai-vault-session-ids.ts.
- accounts-search.ts: the Grok and Cursor search entries move to
  accounts-search-extra-providers.ts and are re-exported.

No behavior change; the affected suites pass.
@nplez1
nplez1 deleted the nplez1/Jenkins-Details branch October 24, 2026 03:23
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