Skip to content

Abort fast when the build box's disk can't hold the run - #178

Merged
bkeepers merged 4 commits into
mainfrom
build-disk-hardening
Aug 17, 2026
Merged

Abort fast when the build box's disk can't hold the run#178
bkeepers merged 4 commits into
mainfrom
build-disk-hardening

Conversation

@bkeepers

@bkeepers bkeepers commented Aug 17, 2026

Copy link
Copy Markdown
Member

Two dispatches died this weekend to box-sizing failures the workflow could have caught in seconds:

  • Run 32024721698 went out on the default server_type: ccx33 (240 GB root disk) with ~8,800 planet jobs remaining. Swap, the docker image, merge scratch, and logs are all sized against the ccx63's 960 GB; the root disk filled in 52 minutes and the runner itself died mid-write (No space left on device: /actions-runner/_diag/...), so the build step never even reported a conclusion.
  • Run 32030051338, the ccx63 retry, died in the scope gate: the hard SIGKILL above left vector-shallow.pmtiles marked incomplete, the main invocation reruns incomplete outputs, but the dry run carried no --rerun-incomplete and refused to plan at all (IncompleteFilesException).

Four changes: one per observed failure mode, plus the shared sizing root, plus a bookkeeping fix from the successful rerun (run 32030835194) whose one-shot status curl let a GitHub 503 paint a fully successful 721-job build as a failed run.

Swapfile scaled to the box

An eighth of the root disk, capped at 64 G. The ccx63 keeps its proven 64 G; a ccx33 gets 30 G instead of spending a quarter of its disk on spill its 32 GB of RAM will rarely reach.

Root-disk floor

The disk_mb budget admits only declared job tmp; swap, image layers, run logs, and under-declared scratch sit outside it on the same disk. After the scope gate and before the main invocation, the step now requires 300 G free for wide plans (>2000 planned jobs, or an unstated scope, which is what a planet dispatch looks like) and 80 G for narrow ones. Bbox runs are narrow by construction and keep the loose floor, so ccx33's documented uses (bbox smokes, stated-scope incremental builds) stay open. The abort message names both remedies: server_type=ccx63, or stating a narrow scope with max_jobs.

On the incident's numbers: ccx33 had ~190 G usable after swap and image, and the wide run consumed all of it, so the 300 G floor rejects that dispatch in seconds. The ccx63 clears it roughly 3x over.

Scope gate plans across incomplete marks

--rerun-incomplete mirrored into the dry run, so a crash-recovery dispatch is planned (with the incomplete outputs as jobs) instead of dying in the gate that exists to protect it.

Status POST retries

The "Publish build status" close is pure bookkeeping; its curl now retries transient API errors (--retry 5 --retry-all-errors) instead of failing the job over a blip.

Validated with actionlint (shellcheck-backed) and the two new shell blocks simulated standalone across ccx63/ccx33 with wide, gated-narrow, and bbox plans.

The root NVMe carries the swapfile, the docker image and its overlay writes,
TMP merge/tippecanoe scratch and the run logs. Only declared job tmp is bounded
by the disk_mb budget, so a wide plan on a small-root box fills the disk out
from under the runner itself: the agent dies on "No space left on device:
/actions-runner/_diag" and the step never reports a conclusion, after an hour
of doomed IO.

Scale the swapfile to an eighth of the root disk instead of a flat 64 G so it
tracks the box, and refuse to start when free root space is under what the
plan's width needs, naming ccx63 as the fix.
The main invocation reruns incomplete outputs, but the dry run refused
to plan at all while one existed (IncompleteFilesException on
vector-shallow.pmtiles, run 32030051338) — so a crash-recovery dispatch
died in the gate that exists to protect it. Mirror --rerun-incomplete
into the dry run.
Copilot AI lite review requested due to automatic review settings August 17, 2026 12:38

Copilot AI 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.

Pull request overview

This PR hardens the build.yml workflow to fail fast when the selected runner’s root disk is too small for the planned run, and to make crash-recovery dispatches plan successfully instead of failing during the scope gate.

Changes:

  • Scale swapfile size to the runner’s root disk (1/8th, capped at 64G) instead of a fixed 64G.
  • Mirror --rerun-incomplete into the scope-gate dry-run so incomplete marks from prior crashes don’t prevent planning.
  • Add a root-disk free-space floor (80G narrow / 300G wide) to abort quickly with actionable remediation guidance.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/build.yml
The gate's dry run executes inside the container and can leave overlay
writes behind, so a floor and disk budget measured before it describe
space that may no longer exist.
The status close is pure bookkeeping, and its one-shot curl let a
single GitHub 503 paint an entirely successful 721-job planet build as
a failed run (run 32030835194).
@bkeepers
bkeepers merged commit 8f8c84f into main Aug 17, 2026
3 checks passed
@bkeepers
bkeepers deleted the build-disk-hardening branch August 17, 2026 16:59
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.

2 participants