I3939 — The executable and the rustdoc surface are built once per run and downloaded by the jobs that drive them
Build the debug executable in one `build` job and the rustdoc surface in one `rustdoc-surface` job, both right after plan, and have the suite shards, evidence, ui and mesh-lab download them through one composite action that falls back to building when the artifact is missing or its provenance is not HEAD.
READY wave 0 · p2 · implementation profile · runs alone
Ready. Every dependency is done, so majordomus plan start I3939 will be accepted.
Objective
Build the debug executable in one `build` job and the rustdoc surface in one `rustdoc-surface` job, both right after plan, and have the suite shards, evidence, ui and mesh-lab download them through one composite action that falls back to building when the artifact is missing or its provenance is not HEAD.
Why
validate.yml builds the same executable in eight jobs (about 4.5 min each) and the rustdoc surface in seven (about 2.3 min each), and lane 3's majordomus-cli artifact is uploaded and never downloaded (run 39762797718). Designed on 2026-10-22: the wall-clock gain is small — about 5 min off evidence at the end of the critical path, shards roughly neutral (a job-level needs cannot overlap their setup with the build), mesh-lab and site 1-1.5 min slower off the path — and the saving is about 30 runner-minutes per full plan (about 4% of 740-860), which also shortens queueing when runs overlap; a compile error fails one job in about 2 min instead of six.
Current state
Built. validate.yml has a `build` job (the debug executable, uploaded as `majordomus-exe` with its provenance: commit, target, rustc, Cargo.lock digest) and a `rustdoc-surface` job (target/web/rustdoc packed, uploaded as `rustdoc-surface`) right after plan, each with the union of its consumers' plan outputs as its `if:`. suite-shard (both), evidence, ui-shard (both), ui-checks (both), ui (the report) and mesh-lab take them through .github/actions/majordomus-bin, which downloads with continue-on-error, restores the mode, checks the provenance against HEAD and the Cargo.lock digest, exports MAJORDOMUS_BIN, and builds the old way (cargo build, ./.github/actions/rustdoc) when an artifact is missing or is another commit's. The ci job needs both producers. macos keeps its own build and the site job its own rustdoc. Case 3939 holds the wiring.
Desired state
The first master run after landing is the measurement: evidence about 5 min shorter on a full plan than on run 39762797718, shards roughly neutral, mesh-lab 1-1.5 min slower off the critical path, about 30 runner-minutes saved per full plan. The per-job rust caches of build and rustdoc-surface start cold once (about 8-10 min).
Scope
- .github/workflows/validate.yml
- .github/actions/majordomus-bin
- test/cases/3939_the_executable_is_built_once_per_run.sh
- .ai/repo/ci/gates.yaml
- .ai/repo/project/issues/I3939.yaml
Out of scope
- .github/actions/rustdoc and pages.yml's use of it (case 97_pages_fast_path pins them)
- rust lane 3's own rustdoc and its majordomus-cli-<triple> artifact (case 401 pins it)
Dependencies
None. This issue is a root of the graph.
Acceptance criteria
- Outside macos, the crate's cargo build appears only in the build job, and a case says so
- Every consumer's plan-output condition is covered by its producer's, shown by a case; a narrow plan skips both producers
- Cases 401, 357, 3019, 3402 and 26 pass; evidence is about 5 min shorter on a full plan than on run 39762797718
Validation
- actionlint .github/workflows/validate.yml
Evidence required
- suite
Evidence
None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.
Risk
Per-job rust caches start cold for the new jobs; the first master run after landing pays about 8-10 min once. upload-artifact drops file modes, hence chmod; v4 refuses a duplicate artifact name in a run, hence majordomus-exe rather than lane 3's name.
Timeline
- started
- —
- verified
- —
- completed
- —
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I3939.yaml. Read it back with majordomus plan show I3939.