31. Fail-Closed Build Dispatch¶
Date: 2026-10-17
Status¶
Accepted. Narrows the execution step of ADR-0015: staging and confirmation stay, a confirmed build now reports that no executor exists.
Context¶
Issue #55: builder.Dispatch returned a successful result for all nine backends (local, gitea, proxmox, gitlab, woodpecker, harbor, minio, jenkins, github) without invoking Packer or a remote API. It built a job id from the clock and formatted an artifact URL. imago build printed that result, and the MCP tools trigger_build and confirm_action returned it, so a confirmed request read as proof that a build had run. A dry run also reported success: true with a trigger timestamp.
Decision¶
- Every request is validated first.
builder.Validaterefuses an unknown flavor or backend in both modes; a cancelled context ends the request before validation. - A request without dry run fails until an executor exists.
Dispatchreturns*builder.BackendUnavailableError, which names the backend and matchesbuilder.ErrBackendUnavailableundererrors.Is. The result hasstatus: "unavailable"andsuccess: false. No I/O is performed. - A dry run reports a plan, not a success. The result has
status: "planned",dry_run: trueandsuccess: false, and its message says that no build was executed. successmeans a backend acknowledged an execution. Fields that only an execution can fill are removed:Result.TriggeredAt,Result.PipelineIDandResult.ArtifactURL.Request.Clockandmcp.Deps.Clockgo with them, since nothing reads a clock any more.- Adapters pass the refusal on.
imago buildexits 1 with the error and prints no build line.trigger_buildwithconfirmed=trueandconfirm_actionon a staged build both return a tool error naming the missing executor; the staged action is consumed, so a second confirmation is refused as before. - A future executor must carry evidence. An executor added for a backend reports success only from what its backend returned (a job id, a run URL, an artifact digest), and adds those fields with it. Validation and confirmation never stand in for that evidence.
Consequences¶
- Automation that called
Dispatch,imago buildortrigger_buildwithout dry run now gets an error where it used to get a fabricated success; a dry-run consumer must readstatusinstead ofsuccess. - Every backend is a planted false-success case:
pkg/builder/builder_test.go,pkg/mcp/build_test.go(confirmed and staged paths) andcmd/imago/main_test.goeach fail if any backend reports success or carries a timestamp, job id or artifact URL. - Images are still built by the
make build-<flavor>targets and the release workflow; this decision changes neither, nor any credential, endpoint or deployment setting.