allocate-disposable and validate are both ubuntu + release-signing with
identical secrets; as separate jobs they paid a second runner warmup and
a second checkout for a step that shares the first's tree. The allocate
steps now run inside validate (guarded by disposable_channel), exposing
the same outputs via steps.allocate; validate keeps its name and output
surface so all 99 downstream needs.validate.outputs.* references are
untouched. A failed allocation fails validate and skips the run, which
is exactly what the old needs-gate produced.