From 1034ab6cb8405a9a17d190181dd71aad88106cad Mon Sep 17 00:00:00 2001 From: meh Date: Fri, 18 Sep 2026 09:36:49 +0700 Subject: [PATCH 1/2] ci: bound the jobs, and stop rebuilding the sidecars every run The Rust job on master ran past twenty minutes with no end in sight. It was not wedged on anything this repository compiles: clippy had been running since 02:33 where the same step takes 65 seconds warm, because a cache entry never existed for it. Every cache in the repository is scoped to `refs/pull/211/merge` and keyed `Darwin-arm64`, which is the macOS panel job. Caches are branch scoped and a branch can only read its own and its base's, so nothing on master ever had one to read, and now that #211 is merged those entries are unreachable anyway. Neither job declared `timeout-minutes`, so the ceiling was GitHub's default of six hours. That is the part that turns a slow step into an invisible one: the run does not fail, it just never finishes, and the queue backs up behind it. Both jobs now have a bound sized to their warm time with room for a cold graph. The sidecars are the other two and a half minutes. Both build outside the workspace target directory, which is what `rust-cache` keys on, so neither was ever cached, and both are the least likely things in the tree to change: the proxy is a fixed-version crates.io install, and the reader is pinned to the v2-era WorkTable it must never move off. They are now cached as the produced binaries, keyed on the manifests, scripts, reader sources and GUI schema that decide them, since the reader's build script derives its schema copy from that. `runner.os` is in the key, so a Linux runner cannot pick up the host-target-suffixed macOS binaries sitting in a developer's checkout. --- .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7c4456e..d1587e99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,10 @@ jobs: # cheap because it runs on every push. The only macOS in this repository # is the release bundle, which cannot be built anywhere else. runs-on: ubicloud-standard-2 + # This job is 1m20s warm and has no network wait that can stall. Without a + # ceiling a hang runs to GitHub's six-hour default, which is how a wedged + # step reads as an afternoon of queued CI rather than a failure. + timeout-minutes: 6 defaults: run: working-directory: apps/gui/frontend @@ -49,6 +53,12 @@ jobs: rust: name: Rust runs-on: ubicloud-standard-4 + # Warm, this job is about five minutes end to end, and cold it is closer to + # fifteen because the whole dependency graph compiles. Twenty leaves room + # for a cold cache after a dependency bump without leaving a genuinely + # wedged step to run for six hours, which is the default and is how a hang + # becomes invisible: the run just never finishes. + timeout-minutes: 20 env: # CI needs diagnostics, not debugger symbol tables. This substantially # shrinks compile and link work without changing which tests execute. @@ -93,10 +103,25 @@ jobs: bun install --no-save bun run build + # Both sidecars build outside the workspace target directory, which is + # what `rust-cache` keys on, so neither was cached and together they cost + # about two and a half minutes of every run. They are also the two things + # least likely to change: the proxy is a fixed-version crates.io install, + # and the reader is pinned to a v2-era WorkTable it must never move off. + # Cache the produced binaries against the inputs that decide them. + - name: Cache the built sidecars + id: sidecars + uses: actions/cache@v4 + with: + path: apps/gui/binaries + key: sidecars-${{ runner.os }}-${{ hashFiles('Cargo.toml', 'crates/wt-migrate/v2-reader/Cargo.toml', 'crates/wt-migrate/v2-reader/**/*.rs', 'apps/gui/src/db/schema/*.rs', 'scripts/stage-agency-proxy-sidecar.sh', 'scripts/stage-wt-v2-reader-sidecar.sh') }} + - name: Build the AgencyProxy sidecar + if: steps.sidecars.outputs.cache-hit != 'true' run: scripts/stage-agency-proxy-sidecar.sh - name: Build the WorkTable v2 migration reader + if: steps.sidecars.outputs.cache-hit != 'true' run: scripts/stage-wt-v2-reader-sidecar.sh # Named per workspace member rather than `--all`, which reaches into any From 3b69129ceb8aa5e4640d69995cb2fafbbc999cdb Mon Sep 17 00:00:00 2001 From: meh Date: Fri, 18 Sep 2026 09:40:44 +0700 Subject: [PATCH 2/2] ci: clippy runs when asked for, not on every push It was the longest step in the Rust job: 65 seconds warm, minutes cold. `--all-targets` type-checks the test and bench targets on top of the lib and bins, which is close to double the compile work, and `cargo test` on the next line then does nearly all of it again. A lint is worth most while the code is still open, which is the machine doing the writing, not one reporting six minutes later. It stays wired up here behind `workflow_dispatch` with a `clippy` input, so the machine's answer is one manual run away when someone wants it. `inputs.clippy` is undefined on push and pull_request, so the step is skipped there without needing a second condition. The exact local command is in the comment above the step, because it was documented nowhere: not in a gates file, not in CLAUDE instructions, and the only mention of clippy in `docs/` is one review from July. --- .github/workflows/ci.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1587e99..f86b8c73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,12 @@ on: push: branches: [master] pull_request: + workflow_dispatch: + inputs: + clippy: + description: "Also run cargo clippy (slow: it type-checks every test target too)" + type: boolean + default: false concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -135,8 +141,22 @@ jobs: # workspace-wide gate. Its compile is covered by the staging step; this is # the formatting half of the same blind spot. - run: cargo fmt --check --manifest-path crates/wt-migrate/v2-reader/Cargo.toml --all + # Clippy runs on request, not on every push. It was the longest step in + # this job, 65 seconds warm and minutes cold, because `--all-targets` + # type-checks the test and bench targets on top of the lib and bins: + # close to double the compile work, nearly all of it repeated by + # `cargo test` below. A lint wants to be read and fixed while the code is + # open, so it belongs on the machine doing the writing: + # + # cargo clippy --workspace --all-targets --no-default-features \ + # --features experimental,webview-runtime -- -D warnings + # + # To get the machine's answer anyway, run this workflow from the Actions + # tab with `clippy: true`. + # # Blitz is currently a macOS preview runtime. Enabling every feature on # this Linux runner asks it to implement Tauri's GTK-only runtime traits, # which is separate portability work rather than Linux CI for AgencyZero. - - run: cargo clippy --workspace --all-targets --no-default-features --features experimental,webview-runtime -- -D warnings + - if: inputs.clippy + run: cargo clippy --workspace --all-targets --no-default-features --features experimental,webview-runtime -- -D warnings - run: cargo test --workspace --no-default-features --features experimental,webview-runtime