Runner Images
The external build pipeline and the ready-image dispatch gate.
Every project pins a runner image: a preset (minimal, frontend, python)
plus tool-version overrides, always including the agent tooling. The image is
built by the external images service — the dashboard only requests the build at
project creation, polls its status about once a minute, and refuses to hand any
queued run to a runner until the build reports complete with a pullable image
reference. Identical specs share one image row and one build.
How to test it
| Suite | Proves |
|---|---|
pnpm vp run @firops/api#test — dispatch-queue.test.ts | A queued run is claimable only once its image build completed with a non-empty reference; pending builds keep the run queued |
Manual smoke: create a project with a fresh spec and dispatch immediately — the
run sits Queued until the image completes, then a runner claims it with no
further action (Dispatching runs).
Decisions
- Building is external — the images service owns Dockerfiles, caching, and registry pushes; the dashboard owns request, status, and the gate. The two sides meet at one HTTP contract, so image tooling evolves without touching the dashboard.
- Gate at claim, not at dispatch — a run for a still-building image is accepted and waits visibly, exactly like a run waiting for a servable provider credential. Rejecting at dispatch would make image latency look like a user error.
- Images dedupe on the spec — two projects with the same preset and overrides share one image row and one build; only overrides that differ from the preset's defaults count, so a no-op pin cannot force a rebuild.
- Project creation fails fast when the builder is unconfigured — a clear
503at the boundary instead of a project whose runs can never start.
Next: Blueprints — the step plan a run snapshots at dispatch.