Skip to content

Per-commit CI bottleneck analysis across PR, candidate-gate, and preview-release workflows (issue-360 rebase outcome) #455

Description

@vishaltandale00

Summary

Records the per-commit CI bottleneck analysis produced while rebasing codex/issue-360-warm-cache-levers onto origin/main (now 601460c, 2026-09-02), the rebase outcome itself, the decision on the one item that needed human judgment, and the post-merge measurements taken on 2026-09-08.

Evidence sources: docs/research/intra-pr-ci-cache.md (hosted timing measurements), docs/agents/ci.md, docs/desktop-release-operations.md, .github/workflows/ci.yml, .github/workflows/desktop-signed-preview.yml, the merged PRs #394, #405, #420, #422, #427, #428, #432, and the hosted runs linked below.

Rebase outcome

Decision (2026-09-08): keep #428's one-runner parallel Vitest, drop the shard (7e4ed3aa)

7e4ed3aa carried no hosted measurements. The first post-#428 main run supplies them (33999200686, 2026-09-05):

Vitest job vitest run wall test/eval-app-integration.test.mjs process-bound tail
125s 78s (176.6s of test CPU across the workers) 61s ~2s (the browser capture is skipped on CI)

The slow file already overlaps the rest of the isolated group on the one runner; it is the tail. A dedicated vitest-slow runner could bring the Vitest wall from ~78s to ~61s (the file's own duration) at ~40s of extra billed setup per run, and #420 found the shard silently broken on top of projects (CLI --exclude does not propagate into a project's resolved exclude). Decision: drop 7e4ed3aa. docs/agents/ci.md already documents the #428 arrangement, so no documentation change follows. The remaining Vitest headroom is inside the slow file (17 tests, 61s), not in runner topology.

Branch housekeeping: local codex/issue-360-warm-cache-levers (7e4ed3aa), backup/pre-rebase-2026-09-02 (e26c845), and backup/pre-rerebase (bd041af1) can be deleted; remote codex/issue-360-warm-cache-levers (306b25f) is merged as #420.

Post-merge measurements (2026-09-08)

The PR-workflow critical path is now packaging

Run Vitest chain ends Packaging ends check starts
main 33999200686 23:42:31 23:43:19 23:43:56
Rust PR 33998956562 23:37:03 23:37:15 23:37:18

A non-Rust, non-desktop PR (runtime cache hit, packaging not selected) now finishes in ~1m25s (33999557594: plan 17s → Vitest 48s → check 9s).

Prime runtime package (macos-arm64) took 275s on main: npm ci 24s, target-cache restore 16s, desktop:pack 213s. Inside desktop:pack:

Phase Time
prepare:renderer + build:packages 8s
pinned Ladybug source staging + static OpenSSL 3.5.8 build ~62s
cargo build --release of the four workspace crates (dependencies fresh from the restored target/) 93s
electron-builder --dir + afterPack verification ~50s

The 93s is structural to the target-archive approach: on a fresh checkout Cargo's mtime fingerprints mark every path crate dirty, so the restored target/ only ever saves dependency compilation. Re-keying the archive would not help (as docs/agents/ci.md already notes). The lever with headroom is the #420 pattern applied to packaging: a digest-keyed, trusted-main-seeded bundle of the two aarch64-apple-darwin release binaries, verified through scripts/ci/runtime-artifact.mjs, installed into target/aarch64-apple-darwin/release/, with desktop:pack skipping the Ladybug/OpenSSL preparation and cargo when a verified bundle is installed and failing open to today's build otherwise. Expected: packaging ~4m35s → ~2m, after which the runtime lane → Vitest chain (~3m50s on Rust PRs, ~2m10s on non-Rust desktop PRs) is the critical path again. Plan pending approval.

4 Sep slow runs: an npm audit stall, not runner contention

Runs 33861852333 (10:09Z) and 33862873159 (10:21Z) took 9–11 minutes instead of ~5. Every job that runs npm ci lost 4–7 minutes in Set up Node dependencies (TypeScript builds 433s, Receipt integrity 430s, Quick 332s, Vitest 313s, packaging 323s); the Rust lanes, which do not run npm ci, were normal. In the slow jobs the ~/.npm cache was an exact hit (3s restore) and the runner image was identical to the fast runs (ubuntu-24.04 20260831.293.1). npm ci printed added 612 packages in 7m with no audited clause, where fast runs print added 631 packages, and audited 637 packages in 16s. npm status posted "npm audits endpoint is having issues" at 11:29Z that day; npm's fetch-timeout default is 300s with two retries, which matches a ~7-minute stall on the audit submission. #439's test-timeout changes address the symptom inside tests; the CI-side mitigation is npm ci --no-audit --no-fund in .github/actions/setup-node-dependencies (nothing in the repository consumes npm audit), which also trims a few seconds from every npm ci.

Cache budget

Repository cache stood at 8.61 GB of the 10 GB limit on 2026-09-08. Deleted 16 stale npm download caches (ten npm-* and six node-cache-* entries for superseded lockfile hashes, ~3.7 GB); usage is now 5.98 GB with only the two live npm-* entries for the current package-lock.json. PR-scoped sccache entries for merged PRs still hold ~2 GB and expire on GitHub's schedule.

Workflow definitions used

  • PR workflow = ci.yml on pull_request: planner -> chapters -> check aggregate.
  • PR release (candidate gate) = Desktop Signed Preview Candidates validate requires a completed successful full-portfolio ci.yml push run on main for the exact candidate commit.
  • Preview workflow release = desktop-signed-preview.yml: validate -> per-target signed/notarized package -> tagged Preview publication.

Per-commit bottleneck mapping

Commit PR workflow bottleneck addressed PR release Preview release
24cc6b1 Cargo timing reports Hidden critical unit: Ladybug build-script = 72-86% of every Rust lane - -
b6f4849 narrow crash lane Crash lane (~12-14 min) fired on non-crash changes Shortens the exact-commit gate run -
52f5036 precise ownership maps Full-mode over-selection: ~11-16 min portfolio where an affected subset suffices Same gate-run saving -
a63b63b deterministic shutdown/skill checks Flake reruns cost a whole lane each A rerun redoes the entire release-commit gate -
7be3547 fix escapes + timings mechanism Wrong timings would select wrong levers - -
2eab738 runtime lane read-only sccache Cache write races: 437-1029 write errors/lane, self-perpetuating cold misses - -
c38a130 record convergence, pin action default Same race; config drift - -
6c7aac1 epoch-reset load-tolerant deadline Load-induced flake -> reruns Rerun risk on release commit -
5d7f8e1 portable deterministic checkpoints Environment-dependent flakes -> reruns Same -
b60542c planner load-tolerant timeout Planner flake under runner contention Same -
1c14d34 unified target dir Ladybug object cache fragmented per lane (281-480 misses -> 116-150) - -
67d13cb pin seal dir, ccache premise Silent ccache launcher switch would destroy cross-run warmth - -
d8f20d5 / a730442 review rounds Mapping, harvest, and rerun integrity - -
5d61f9b retrigger run Ops only - -
Docs commits (54fe9f7, 9498bde, e22aa6a, 99ebd4e) Evidence: lane timings; 244-328 s Ladybug floor is cache overhead not compile (43 s warm local) - -
54637fd Ladybug built once (#394) Single biggest bottleneck removed: Ladybug CMake build 244-328 s x 4 lanes -> build once, verify, restore from cache/artifact Gate run paid the same floor -> candidates waited on it - (preview builds on macOS runners, separate path)
e797664 harden prebuilt bundle New failure mode (bad/stale bundle) must fail open, never block a lane - -
fcaba5d + e26c845 cache levers, shard, review round 2 (a) Runtime bundle missed structurally for narrow plans -> full runtime-lane build on every small PR; (b) slow Vitest file extends the Vitest wall; (c) under-covering bundle could store/install; empty-shard guard Shorter exact-commit gate run -
e26c845 portfolio-constant keying (#420) Narrow plans structurally missed the seeded runtime bundle - "the exact small-change PRs the lever accelerates" Same -
7e4ed3aa rebased residual (dropped 2026-09-08) Slow file test/eval-app-integration.test.mjs still ends the Vitest wall after #428's parallel config; a second runner would recover ~17s, see Decision above - -

Biggest bottlenecks per workflow today (post-merge, 2026-09-08)

  1. PR workflow: (a) the packaging job's release rebuild plus Ladybug/OpenSSL preparation (~155s of a 275s job; the current critical path on main and on Rust PRs); (b) changed-unit Rust misses (~100-114 s per workspace crate; the Ladybug floor is gone); (c) the runtime lane -> Vitest chain (~97s + ~125s), which becomes critical once packaging drops; (d) fail-open full-mode selection for unmapped paths (by design, ~11-16 min); (e) npm ci exposure to registry-side stalls through the audit submission.
  2. PR release (candidate gate): structural, not code - the candidate waits for the full-portfolio main CI run on the exact commit (fails closed; ~6 min warm after ci: warm the cache levers on top of the parallel Vitest portfolio #420, longer cold), and any flake reruns the whole gate on the release commit.
  3. Preview release: validate chains on that same main run; then cargo fetch --locked network fetch (mitigated by Seed the locked Cargo closure before the offline preview build #427's closure seeding); then the ~3m46s-4m18s package build plus external Apple signing/notarization latency; publication is gated on tag-annotation integrity (Restore the annotated tag object before reading its candidate pin #432), not time.

Suggested next steps

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-humanReady for human review or action

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions