From 0bf0a89ba340715135fb32545809501bbc7b49e5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 21:02:52 +0000 Subject: [PATCH 1/7] WIP R1: seed job builds or restores the compiler pair once per run --- dag/gunbc/witness/compiler_gate_workflow.dag | 367 +++++++++++++------ 1 file changed, 261 insertions(+), 106 deletions(-) diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index 3d0913bf306..bc7a91c95a7 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -144,7 +144,15 @@ import gunbc.heal_candidate { import gunbc.heal_publication_boundary { heal_repair_candidate_artifact_name } // The fork clauses are heal's authorship; the fleet lane inherited the work they guarded. import gunbc.heal_workflow { heal_same_repository_clause, heal_not_dependabot_clause } +import gunbc.fleet_workflow_steps { + ci_cache_cargo_step, ci_release_bins_lookup_step, ci_release_bins_cache_step, + ci_release_bins_upload_step, ci_release_bins_download_step, gunbc_ci_aux_step_timeout_minutes, +} import gunbc.fleet_release_bins_key { + release_bins_outcome_script_for, release_bins_consumer_key_check_script_for, + release_bins_key_expression, release_bins_key_step_id, release_bins_outcome_step_id, + release_bins_lookup_standing_expression, release_bins_lookup_entry_expression, release_bins_cache_hit_expression, + release_bins_key_job_output, release_bins_build_condition, compiler_pair_release_product, release_bins_key_script_for, release_bins_pack_manifest_script_for, release_bins_pack_object_script, release_bins_pack_object_path_expression, compiler_pair_candidate_artifact_name, compiler_pair_candidate_retention_whole_days, @@ -739,12 +747,7 @@ fn compiler_gate_candidate_upload_step() -> Step { } fn compiler_gate_floor_bound_steps() -> List { - concat(compiler_gate_fleet_prelude_bound_steps_around_build( - bins: witness_floor_required_bins, - build_step_name: compiler_gate_floor_build_step_name, - before_build: compiler_gate_candidate_key_bound_steps(), - after_build: compiler_gate_candidate_pack_bound_steps(), - ), concat([ + concat(compiler_gate_fleet_prelude_bound_steps(), concat([ CompilerGateBoundStep { step: compiler_gate_floor_run_step(), role: consumes_only(capabilities: [RustfmtCapability]), @@ -753,24 +756,9 @@ fn compiler_gate_floor_bound_steps() -> List { ], compiler_gate_floor_measurement_bound_steps())) } -// THE PRELUDE BOTH FLEET JOBS NEED, ONE LIST RATHER THAN TWO COPIES: the head checkout, isolated -// toolchain homes, a pinned default, and claim_executor plus gunbc built from the judged tree. The -// build step keeps its id, build_witness_fold, in both jobs because every downstream condition in -// each job reads that id; step ids are per job, so the two never collide. -fn compiler_gate_fleet_prelude_bound_steps() -> List { - compiler_gate_fleet_prelude_bound_steps_around_build( - bins: witness_floor_required_bins, - build_step_name: compiler_gate_floor_build_step_name, - before_build: [], - after_build: [], - ) -} - -// emit-build needs only `gunbc` (its instruments run through `gunbc test`), so it takes the same -// prelude over its own binaries rather than a copy of it; the floor adds its candidate-key and -// candidate-pack steps around the same build. -fn compiler_gate_fleet_prelude_bound_steps_around_build(bins: List, build_step_name: String, before_build: List, after_build: List) -> List { - concat([ +// THE HEAD OF EVERY FLEET JOB: the judged checkout, an isolated toolchain home and a pinned default. +fn compiler_gate_fleet_head_bound_steps() -> List { + [ CompilerGateBoundStep { step: compiler_gate_floor_checkout_step(), role: capability_neutral, @@ -791,22 +779,33 @@ fn compiler_gate_fleet_prelude_bound_steps_around_build(bins: List, buil role: consumes_only(capabilities: [RustupCapability, CargoCapability]), step_name: "Pin rustup default (isolated RUSTUP_HOME has no default toolchain)", }, - ], concat(before_build, concat([ + ] +} + +// THE PRELUDE EVERY CONSUMER LANE NEEDS: the head, then the compiler pair the `seed` job built or +// restored ONCE for the run, verified against the seed's key and promoted into this job's OWN +// checkout (target/release is private to the job, so a native probe still runs against a writable +// directory nobody else shares). The restore step keeps the id build_witness_fold because every +// downstream condition in each job reads that id: `success` there now means "the pair was verified +// and promoted", which is the precondition those conditions always stood for. +fn compiler_gate_fleet_prelude_bound_steps() -> List { + concat(compiler_gate_fleet_head_bound_steps(), [ CompilerGateBoundStep { - step: witness_floor_build_step( - step_name: build_step_name, - build_script: repo_self_build_command(bins: bins), - ), + step: ci_release_bins_download_step(), + role: capability_neutral, + step_name: "Download release-bins artifact", + }, + CompilerGateBoundStep { + step: compiler_gate_pair_restore_step(), role: consumes_only(capabilities: [CargoCapability]), - step_name: build_step_name, + step_name: compiler_gate_pair_restore_step_name, }, - ], concat(after_build, [ CompilerGateBoundStep { step: witness_toolchain_homes_isolated_guard_step(), role: capability_neutral, step_name: "Require isolated toolchain homes", }, - ])))) + ]) } // ORDER: the lint first (the cheapest verdict, over the Rust the prelude just built), then the build @@ -905,74 +904,6 @@ fn compiler_gate_candidate_condition() -> String { "github.event_name == 'merge_group'" } -fn compiler_gate_candidate_built_condition() -> String { - "github.event_name == 'merge_group' && steps.build_witness_fold.outcome == 'success'" -} - -fn compiler_gate_candidate_run_step(name: String, script: String, condition: String) -> Step { - RunStep { - name: Present { value: name }, - id: none, - run: script, - shell: none, - env: none, - working_directory: none, - if_condition: Present { value: condition }, - continue_on_error: none, - timeout_minutes: none - } -} - -fn compiler_gate_candidate_key_bound_steps() -> List { - [ - CompilerGateBoundStep { - step: compiler_gate_candidate_run_step( - name: "Key the compiler pair candidate (merge group; stamps the build with the materialized tree)", - script: release_bins_key_script_for(product: compiler_pair_release_product()), - condition: compiler_gate_candidate_condition(), - ), - role: consumes_only(capabilities: [CargoCapability, RustcCapability]), - step_name: "Key the compiler pair candidate (merge group; stamps the build with the materialized tree)", - }, - ] -} - -fn compiler_gate_candidate_pack_bound_steps() -> List { - [ - CompilerGateBoundStep { - step: compiler_gate_candidate_run_step( - name: "Pack the compiler pair candidate with its build log", - script: join([release_bins_pack_manifest_script_for(product: compiler_pair_release_product()), release_bins_pack_object_script()], "\n"), - condition: compiler_gate_candidate_built_condition(), - ), - role: capability_neutral, - step_name: "Pack the compiler pair candidate with its build log", - }, - CompilerGateBoundStep { - step: UsesStep { - name: Present { value: "Upload the compiler pair candidate" }, - id: none, - uses: upload_artifact_action, - with: Present { - value: [ - kv(key: "name", value: yaml_string(s: compiler_pair_candidate_artifact_name)), - kv(key: "path", value: yaml_string(s: release_bins_pack_object_path_expression())), - kv(key: "if-no-files-found", value: yaml_string(s: "error")), - kv(key: "retention-days", value: yaml_int(n: compiler_pair_candidate_retention_whole_days())), - kv(key: "compression-level", value: yaml_int(n: 0)) - ] - }, - env: none, - if_condition: Present { value: compiler_gate_candidate_built_condition() }, - continue_on_error: none, - timeout_minutes: none - }, - role: capability_neutral, - step_name: "Upload the compiler pair candidate", - }, - ] -} - fn compiler_gate_floor_job() -> Job { Job { id: compiler_gate_floor_job_id, @@ -980,7 +911,7 @@ fn compiler_gate_floor_job() -> Job { runner: gunbc_ci_selected_runner_spec(), environment: none, steps: list_map(xs: compiler_gate_floor_bound_steps(), f: fn(b) { b.step }), - needs: [], + needs: [compiler_gate_seed_job_id], env: Present { value: [ kv(key: malloc_arena_max_env_key, value: yaml_string(s: malloc_arena_max_value)), @@ -1040,7 +971,7 @@ fn compiler_gate_generated_job() -> Job { runner: gunbc_ci_selected_runner_spec(), environment: none, steps: list_map(xs: compiler_gate_generated_bound_steps(), f: fn(b) { b.step }), - needs: [], + needs: [compiler_gate_seed_job_id], env: Present { value: [ kv(key: malloc_arena_max_env_key, value: yaml_string(s: malloc_arena_max_value)) @@ -1165,7 +1096,7 @@ fn compiler_gate_emit_build_subject_bound_steps() -> List // it), a pinned default, and the `gunbc` binary the instruments are invoked through. fn compiler_gate_emit_build_bound_steps() -> List { concat( - concat(compiler_gate_fleet_prelude_bound_steps_around_build(bins: compiler_gate_required_bins, build_step_name: compiler_gate_build_step_name, before_build: [], after_build: []), compiler_gate_emit_build_subject_bound_steps()), + concat(compiler_gate_fleet_prelude_bound_steps(), compiler_gate_emit_build_subject_bound_steps()), [CompilerGateBoundStep { step: compiler_gate_emit_build_notice_step(), role: capability_neutral, @@ -1192,7 +1123,7 @@ fn compiler_gate_emit_build_job() -> Job { runner: gunbc_ci_selected_runner_spec(), environment: none, steps: list_map(xs: compiler_gate_emit_build_bound_steps(), f: fn(b) { b.step }), - needs: [], + needs: [compiler_gate_seed_job_id], env: Present { value: [ kv(key: malloc_arena_max_env_key, value: yaml_string(s: malloc_arena_max_value)) @@ -1207,6 +1138,221 @@ fn compiler_gate_emit_build_job() -> Job { } } +// ═══════════════════════════════════════════════════════════════════════════ +// THE SEED LANE: THE COMPILER PAIR IS BUILT OR RESTORED ONCE PER RUN. +// +// OPERATOR DIRECTION 2026-10-06 (CI programme item 1, rows R1/R2 of docs/plans/ci-end-to-end-accounting.md): +// the three fleet lanes each built the same two binaries from the same tree, so a run paid the +// release build three times on three runners. The pair is one product keyed by +// gunbc.fleet_release_bins_key compiler_pair_release_product -- the same keyed materialization +// fleet-converge's build job uses, so the cache lookup, the restore into staging, the complete +// verification before any restored binary runs and the build-log member are that module's, not +// re-derived here. This job derives the key, looks it up (admitted refs only), builds ONLY on a miss +// (release_bins_build_condition), packs on a build and hands the pack to the three consumers as a +// workflow artifact. A consumer verifies the pack against this job's key and promotes it into its +// OWN checkout's target/release, so every consumer still runs the binaries from a private writable +// directory; nothing is shared by path. +// +// A MISS IS THE FULL BUILD, NOT A LOSS OF COVERAGE: the build line is the product's own build_line, +// the very text the key names. A lookup that cannot answer is not absence (the lookup step says so), +// and a pack that does not verify refuses. The job is a roster growth the operator authorized for +// this programme; it replaces two of three builds per run. +data compiler_gate_seed_job_id: String = "seed" + +data compiler_gate_seed_build_step_name: String = "Build the compiler pair (keyed miss only)" +data compiler_gate_pair_restore_step_name: String = "Verify the seed's compiler pair against its key and promote it into this checkout" + +fn compiler_gate_seed_run_step(name: String, id: String?, script: String, env: List?, condition: String?, timeout: Int?) -> Step { + RunStep { + name: Present { value: name }, + id: id, + run: script, + shell: none, + env: env, + working_directory: none, + if_condition: condition, + continue_on_error: none, + timeout_minutes: timeout + } +} + +fn compiler_gate_seed_key_expression() -> String { + join(["$", "{{ needs.", compiler_gate_seed_job_id, ".outputs.", release_bins_key_job_output, " }}"], "") +} + +fn compiler_gate_pair_restore_step() -> Step { + compiler_gate_seed_run_step( + name: compiler_gate_pair_restore_step_name, + id: Present { value: "build_witness_fold" }, + script: release_bins_consumer_key_check_script_for(product: compiler_pair_release_product()), + env: Present { value: [kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: compiler_gate_seed_key_expression()))] }, + condition: none, + timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, + ) +} + +fn compiler_gate_seed_bound_steps() -> List { + concat(compiler_gate_fleet_head_bound_steps(), [ + CompilerGateBoundStep { + step: compiler_gate_seed_run_step( + name: "Key the compiler pair (stamps the build with the materialized tree)", + id: Present { value: release_bins_key_step_id }, + script: release_bins_key_script_for(product: compiler_pair_release_product()), + env: none, + condition: none, + timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, + ), + role: consumes_only(capabilities: [CargoCapability, RustcCapability]), + step_name: "Key the compiler pair (stamps the build with the materialized tree)", + }, + CompilerGateBoundStep { + step: ci_release_bins_lookup_step(), + role: capability_neutral, + step_name: "Look up the compiler pair by exact key", + }, + CompilerGateBoundStep { + step: ci_release_bins_cache_step(), + role: capability_neutral, + step_name: "Restore the compiler pair into isolated staging", + }, + CompilerGateBoundStep { + step: compiler_gate_seed_run_step( + name: "Decide the compiler pair outcome; verify a stored pack completely before any restored binary runs", + id: Present { value: release_bins_outcome_step_id }, + script: release_bins_outcome_script_for(product: compiler_pair_release_product()), + env: Present { + value: [ + kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: release_bins_key_expression())), + kv(key: "LOOKUP_STANDING", value: yaml_string(s: release_bins_lookup_standing_expression())), + kv(key: "LOOKUP_ENTRY", value: yaml_string(s: release_bins_lookup_entry_expression())), + kv(key: "CACHE_HIT", value: yaml_string(s: release_bins_cache_hit_expression())), + ] + }, + condition: none, + timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, + ), + role: consumes_only(capabilities: [CargoCapability]), + step_name: "Decide the compiler pair outcome", + }, + CompilerGateBoundStep { + step: ci_cache_cargo_step(), + role: capability_neutral, + step_name: "Cache Cargo", + }, + CompilerGateBoundStep { + step: compiler_gate_seed_run_step( + name: compiler_gate_seed_build_step_name, + id: Present { value: "build_witness_fold" }, + script: concat(wrap_command_with_floor_attempt_receipt(command: compiler_pair_release_product().build_line), "\n"), + env: none, + condition: Present { value: release_bins_build_condition() }, + timeout: none, + ), + role: consumes_only(capabilities: [CargoCapability]), + step_name: compiler_gate_seed_build_step_name, + }, + CompilerGateBoundStep { + step: compiler_gate_seed_run_step( + name: "Pack the compiler pair with its build log (build outcomes only)", + id: none, + script: release_bins_pack_manifest_script_for(product: compiler_pair_release_product()), + env: Present { value: [kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: release_bins_key_expression()))] }, + condition: Present { value: release_bins_build_condition() }, + timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, + ), + role: capability_neutral, + step_name: "Pack the compiler pair with its build log (build outcomes only)", + }, + CompilerGateBoundStep { + step: ci_release_bins_upload_step(), + role: capability_neutral, + step_name: "Upload release-bins artifact", + }, + CompilerGateBoundStep { + step: compiler_gate_seed_run_step( + name: "Wrap the compiler pair as the merge-group candidate object", + id: none, + script: release_bins_pack_object_script(), + env: none, + condition: Present { value: compiler_gate_candidate_condition() }, + timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, + ), + role: capability_neutral, + step_name: "Wrap the compiler pair as the merge-group candidate object", + }, + CompilerGateBoundStep { + step: UsesStep { + name: Present { value: "Upload the compiler pair candidate" }, + id: none, + uses: upload_artifact_action, + with: Present { + value: [ + kv(key: "name", value: yaml_string(s: compiler_pair_candidate_artifact_name)), + kv(key: "path", value: yaml_string(s: release_bins_pack_object_path_expression())), + kv(key: "if-no-files-found", value: yaml_string(s: "error")), + kv(key: "retention-days", value: yaml_int(n: compiler_pair_candidate_retention_whole_days())), + kv(key: "compression-level", value: yaml_int(n: 0)) + ] + }, + env: none, + if_condition: Present { value: compiler_gate_candidate_condition() }, + continue_on_error: none, + timeout_minutes: none + }, + role: capability_neutral, + step_name: "Upload the compiler pair candidate", + }, + CompilerGateBoundStep { + step: witness_toolchain_homes_isolated_guard_step(), + role: capability_neutral, + step_name: "Require isolated toolchain homes", + }, + ]) +} + +fn compiler_gate_seed_capability_closure_holds() -> Bool { + capability_closure_is_closed( + v: workflow_job_capability_closure( + steps: list_map( + xs: compiler_gate_seed_bound_steps(), + f: fn(b) { CapabilityAnnotatedStep { step_name: b.step_name, role: b.role } }, + ) + ), + ) +} + +fn compiler_gate_seed_job() -> Job { + Job { + id: compiler_gate_seed_job_id, + name: none, + runner: gunbc_ci_selected_runner_spec(), + environment: none, + steps: list_map(xs: compiler_gate_seed_bound_steps(), f: fn(b) { b.step }), + needs: [], + env: Present { + value: [ + kv(key: malloc_arena_max_env_key, value: yaml_string(s: malloc_arena_max_value)) + ] + }, + outputs: Present { + value: [kv(key: release_bins_key_job_output, value: yaml_string(s: release_bins_key_expression()))] + }, + if_condition: Present { value: compiler_gate_floor_job_condition() }, + timeout_minutes: Present { value: compiler_gate_floor_timeout_minutes }, + continue_on_error: none, + concurrency: none, + permissions: Present { + value: WorkflowPermissions { + contents: Present { value: PermRead }, + pull_requests: none, + issues: none, + actions: Present { value: PermRead }, + id_token: none, + } + } + } +} + // A LANE RESULT IS AN EXPRESSION, NOT SHELL TEXT, AND THE JOB ID IS NOT ALWAYS DOT-SAFE. // // `-` is the SUBTRACTION OPERATOR in the Actions expression language, so `needs.emit-build.result` @@ -1524,6 +1670,15 @@ fn compiler_gate_lane_rows() -> List { flip_trigger: "gunbc.rung_drop rust_unit_tests_over_their_cap: the full v1 --lib unit suite completes under its cap on the fleet runner on every head. Sufficient for: the lane red meaning a test failed, never that its clock ran out." }, }, + CompilerGateLaneRow { + job_id: compiler_gate_seed_job_id, + var_name: "SEED", + standing: LaneBlocks, + arm: GateArmSkippableOnFork { + notice: "the seed lane is skipped for a fork pull request: no compiler pair was built or restored for this head", + unobserved_error: "the seed lane produced no success" + } + }, CompilerGateLaneRow { job_id: compiler_gate_emit_build_job_id, var_name: "EMIT_BUILD", @@ -1683,7 +1838,7 @@ data compiler_gate_workflow: Workflow = Workflow { kv(key: (required_ci_contract_epoch_env_key as String), value: yaml_string(s: (required_ci_contract_epoch as String))) ] }, permissions: Present { value: compiler_gate_permissions }, - jobs: [compiler_gate_unit_test_job(), compiler_gate_emit_build_job(), compiler_gate_floor_job(), compiler_gate_generated_job(), compiler_gate_aggregate_job()] + jobs: [compiler_gate_unit_test_job(), compiler_gate_seed_job(), compiler_gate_emit_build_job(), compiler_gate_floor_job(), compiler_gate_generated_job(), compiler_gate_aggregate_job()] } data compiler_gate_capability_refusal_reason: String = "REFUSED gunbc.compiler_gate_workflow: a job's capability closure does not hold, a floor script is not renderable, the workflow runs a job that compiler_gate_lane_rows does not roster, or a blocking lane or the aggregate runs on a hosted runner (compiler_gate_merge_path_runs_on_the_fleet, operator ruling 2026-09-28). No workflow yaml was emitted. Fix the step roles in the lane's bound steps, add the job's row, or put the merge-path job on gunbc_ci_selected_runner_spec. The gate's needs, env bindings and every conditional are FOLDED from that roster, so they cannot disagree with it and this refusal has nothing to say about them." @@ -1701,7 +1856,7 @@ fn expected_compiler_gate_yml() -> WitnessFloorGenerationOutcome { WorkflowToolchainHomeRefused { job_id: j, refusal: r } => WitnessFloorGenerationRefused { reason: toolchain_home_refusal_reason(job_id: j, refusal: r) } WorkflowToolchainHomesAdmitted => - if compiler_gate_unit_test_capability_closure_holds() && compiler_gate_emit_build_capability_closure_holds() && compiler_gate_floor_capability_closure_holds() && compiler_gate_generated_capability_closure_holds() && compiler_gate_floor_scripts_are_renderable() && compiler_gate_lane_standings_hold() && compiler_gate_merge_path_runs_on_the_fleet() { + if compiler_gate_unit_test_capability_closure_holds() && compiler_gate_emit_build_capability_closure_holds() && compiler_gate_seed_capability_closure_holds() && compiler_gate_floor_capability_closure_holds() && compiler_gate_generated_capability_closure_holds() && compiler_gate_floor_scripts_are_renderable() && compiler_gate_lane_standings_hold() && compiler_gate_merge_path_runs_on_the_fleet() { match emit_yaml(v: project_workflow_to_yaml(workflow: compiler_gate_workflow)) { EmittedYaml { text: t } => WitnessFloorGenerated { content: t } YamlEmitRefused { path: p, reason: r } => WitnessFloorGenerationRefused { reason: yaml_emit_refusal_text(module_path: "gunbc.compiler_gate_workflow", path: p, reason: r) } From 96dd88ac761cf992a471c7226c14e09d80bce8b7 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 21:23:16 +0000 Subject: [PATCH 2/7] R1: regenerate witnesses.yml with the seed job; fix env typing --- .github/workflows/witnesses.yml | 570 ++++++++++++++----- dag/gunbc/witness/compiler_gate_workflow.dag | 28 +- 2 files changed, 433 insertions(+), 165 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index c2d590b6b55..f14e953e20f 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -77,12 +77,15 @@ jobs: 'exit' "$GUNBC_FLOOR_EXIT" timeout-minutes: 40 - emit-build: + seed: runs-on: [self-hosted, linux, arm64] timeout-minutes: 90 if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository permissions: contents: read + actions: read + outputs: + release_bins_key: ${{ steps.release_bins_key.outputs.key }} steps: - name: Checkout the commit this lane judges uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 @@ -110,123 +113,8 @@ jobs: if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" - - name: Build the compiler - id: build_witness_fold - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'cargo build --release -p v1-compiler --bin gunbc') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" - GUNBC_FLOOR_EXIT="$?" - GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' - GUNBC_FLOOR_CLASS='structural' - GUNBC_FLOOR_SIGNATURE='' - if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi - if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi - 'exit' "$GUNBC_FLOOR_EXIT" - - - name: Require isolated toolchain homes - run: | - if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi - if: "!cancelled()" - - name: emit and build //gunbc/instruments:self-host - id: emit_build_self_host - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' ''\''target/release/gunbc'\'' '\''test'\'' '\''//gunbc/instruments:self-host'\''') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" - GUNBC_FLOOR_EXIT="$?" - GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' - GUNBC_FLOOR_CLASS='structural' - GUNBC_FLOOR_SIGNATURE='' - if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi - if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi - 'exit' "$GUNBC_FLOOR_EXIT" - - - name: emit and build //gunbc/instruments:v2-native-cli - id: emit_build_v2_native_cli - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' ''\''target/release/gunbc'\'' '\''test'\'' '\''//gunbc/instruments:v2-native-cli'\''') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" - GUNBC_FLOOR_EXIT="$?" - GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' - GUNBC_FLOOR_CLASS='structural' - GUNBC_FLOOR_SIGNATURE='' - if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi - if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi - 'exit' "$GUNBC_FLOOR_EXIT" - - - name: Why this lane is red, and what would be a real finding - run: |- - printf '%s\n' 'emit-build is a REQUIRED lane: a red here BLOCKS your merge, through the required context `witnesses`.' - printf '%s\n' 'No standing break is declared, so a red here is a REAL FINDING -- most likely yours.' - if: failure() - env: - MALLOC_ARENA_MAX: "2" - floor: - runs-on: [self-hosted, linux, arm64] - timeout-minutes: 90 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository - permissions: - contents: read - steps: - - name: Checkout the commit this lane judges - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - - name: Isolate toolchain homes - run: |- - # dissolve-on: ci_toolchain_home_isolation_script -- orch-emitted foreign-executor prelude step wiping and setting HOME/CARGO_HOME/RUSTUP_HOME under RUNNER_TEMP so concurrent runner slots stop sharing one toolchain; leaf rm/echo strings remain until a typed per-job filesystem-and-environment effect lands on host_effect_apply (shell-to-intent Phase 2). This obligation covers THIS carrier and ci_isolate_toolchain_script, which share that terminal construction; ci_pin_rustup_default_script carries its own obligation because it does not - rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo" - echo "HOME=$RUNNER_TEMP" >> "$GITHUB_ENV" - echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" - echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" - - name: Install Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 - with: - components: rustfmt - cache: false - rustflags: -D warnings - - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) - run: |- - # dissolve-on: ci_pin_rustup_default_script -- orch-emitted foreign-executor step selecting a rustup default toolchain inside an isolated RUSTUP_HOME, which starts with none, and resolving the cargo binary that selection implies. The leaf rustup/command/echo strings remain until a typed TOOLCHAIN-SELECTION effect lands on host_effect_apply -- NOT the filesystem-and-environment effect ci_toolchain_home_isolation_script waits on, which is why this is a separate obligation: that effect landing alone would leave this carrier standing - rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" - if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi - if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi - echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" - - name: Key the compiler pair candidate (merge group; stamps the build with the materialized tree) + - name: Key the compiler pair (stamps the build with the materialized tree) + id: release_bins_key run: | set -e ROOT=$('git' 'rev-parse' '--show-toplevel') @@ -309,8 +197,103 @@ jobs: 'cat' "$EXPECTED" 'echo' 'key='"$KEY" | 'tee' '-a' "$GITHUB_OUTPUT" > '/dev/null' 'echo' 'GUNBC_MATERIALIZED_TREE_IDENTITY='"$SOURCE_CLOSURE" | 'tee' '-a' "$GITHUB_ENV" > '/dev/null' - if: github.event_name == 'merge_group' - - name: Build the compiler and the witness executor + timeout-minutes: 5 + - name: Look up release-bins by exact key under the admitted producer refs (cache API; a failed lookup is not absence) + id: release_bins_lookup + run: | + set -e + ROOT=$('git' 'rev-parse' '--show-toplevel') + STANDING='absent' + ENTRY='none' + 'curl' '-sSf' '-H' 'Authorization: Bearer '"$GH_TOKEN" '-H' 'Accept: application/vnd.github+json' "$GITHUB_API_URL"'/repos/'"$GITHUB_REPOSITORY"'/actions/caches?per_page=100&key='"$RELEASE_BINS_KEY"'&ref=''refs/heads/main' '-o' "$ROOT"'/.release-bins-lookup-main.json' || ('echo' '::error title=release-bins LookupTransportFailed::the cache API lookup FAILED (unreachable or refused); this is not absence, so neither reuse nor rebuild is decided'; exit 1) + 'jq' '--raw-output' '--exit-status' '. as $answer | [inputs] | if length != 0 then error("the cache-API answer is not exactly one JSON document") else $answer | if type == "object" and (.total_count | type) == "number" and (.actions_caches | type) == "array" and .total_count == (.actions_caches | length) and .total_count <= 100 and all(.actions_caches[]; type == "object" and (.key | type) == "string" and (.ref | type) == "string") then [.actions_caches[] | select(.key == $ENV.RELEASE_BINS_KEY and .ref == "refs/heads/main")] | if length == 0 then "absent" elif length == 1 then "present\nid=\(.[0].id) ref=\(.[0].ref) version=\(.[0].version) created=\(.[0].created_at)" else error("duplicate exact entries") end else error("cache-API answer is not the admitted shape") end end' '.release-bins-lookup-main.json' > "$RUNNER_TEMP"'/lookup-main.decoded' || ('echo' '::error title=release-bins LookupAnswerUnusable::the cache API answer was unreadable or incomplete; it is not treated as absence'; exit 1) + DECODED_STANDING=$('head' '-n' '1' "$RUNNER_TEMP"'/lookup-main.decoded') + if '[' "$DECODED_STANDING" '=' 'present' ']'; then STANDING='present'; ENTRY=$('sed' '-n' '2p' "$RUNNER_TEMP"'/lookup-main.decoded'); else '[' "$DECODED_STANDING" '=' 'absent' ']' || ('echo' '::error title=release-bins LookupAnswerUnusable::the cache API answer was unreadable or incomplete; it is not treated as absence'; exit 1); fi + 'curl' '-sSf' '-H' 'Authorization: Bearer '"$GH_TOKEN" '-H' 'Accept: application/vnd.github+json' "$GITHUB_API_URL"'/repos/'"$GITHUB_REPOSITORY"'/actions/caches?per_page=100&key='"$RELEASE_BINS_KEY"'&ref='"$RELEASE_BINS_DISPATCH_SCOPE_REF" '-o' "$ROOT"'/.release-bins-lookup-dispatch.json' || ('echo' '::error title=release-bins LookupTransportFailed::the cache API lookup FAILED (unreachable or refused); this is not absence, so neither reuse nor rebuild is decided'; exit 1) + 'jq' '--raw-output' '--exit-status' '. as $answer | [inputs] | if length != 0 then error("the cache-API answer is not exactly one JSON document") else $answer | if type == "object" and (.total_count | type) == "number" and (.actions_caches | type) == "array" and .total_count == (.actions_caches | length) and .total_count <= 100 and all(.actions_caches[]; type == "object" and (.key | type) == "string" and (.ref | type) == "string") then [.actions_caches[] | select(.key == $ENV.RELEASE_BINS_KEY and .ref == $ENV.RELEASE_BINS_DISPATCH_SCOPE_REF)] | if length == 0 then "absent" elif length == 1 then "present\nid=\(.[0].id) ref=\(.[0].ref) version=\(.[0].version) created=\(.[0].created_at)" else error("duplicate exact entries") end else error("cache-API answer is not the admitted shape") end end' '.release-bins-lookup-dispatch.json' > "$RUNNER_TEMP"'/lookup-dispatch.decoded' || ('echo' '::error title=release-bins LookupAnswerUnusable::the cache API answer was unreadable or incomplete; it is not treated as absence'; exit 1) + DECODED_STANDING=$('head' '-n' '1' "$RUNNER_TEMP"'/lookup-dispatch.decoded') + if '[' "$DECODED_STANDING" '=' 'present' ']'; then STANDING='present'; ENTRY=$('sed' '-n' '2p' "$RUNNER_TEMP"'/lookup-dispatch.decoded'); else '[' "$DECODED_STANDING" '=' 'absent' ']' || ('echo' '::error title=release-bins LookupAnswerUnusable::the cache API answer was unreadable or incomplete; it is not treated as absence'; exit 1); fi + 'echo' 'standing='"$STANDING" | 'tee' '-a' "$GITHUB_OUTPUT" > '/dev/null' + 'echo' 'entry='"$ENTRY" | 'tee' '-a' "$GITHUB_OUTPUT" > '/dev/null' + 'echo' 'release-bins lookup: standing='"$STANDING"' entry='"$ENTRY" + env: + RELEASE_BINS_KEY: ${{ steps.release_bins_key.outputs.key }} + GH_TOKEN: ${{ github.token }} + RELEASE_BINS_DISPATCH_SCOPE_REF: ${{ startsWith(github.ref, 'refs/tags/') && format('refs/heads/{0}', github.ref) || github.ref }} + timeout-minutes: 5 + - name: Restore release-bins into isolated staging (transport only; publishes on a verified build) + id: release_bins_cache + uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 + with: + path: |- + .release-bins-staging/release-bins.tgz + .release-bins-staging/release-bins.preimage + .release-bins-staging/release-bins.manifest + key: ${{ steps.release_bins_key.outputs.key }} + timeout-minutes: 10 + - name: Decide the compiler pair outcome; verify a stored pack completely before any restored binary runs + id: release_bins_outcome + run: | + set -e + ROOT=$('git' 'rev-parse' '--show-toplevel') + STAGE="$ROOT"'/.release-bins-staging' + EXPECTED="$RUNNER_TEMP"'/release-bins.preimage.expected' + if '[' "$LOOKUP_STANDING" '=' 'present' ']' && '[' "$CACHE_HIT" '=' 'true' ']'; then ':' 'no-op'; else if '[' "$LOOKUP_STANDING" '=' 'present' ']' && '[' "$CACHE_HIT" '!=' 'true' ']'; then ('echo' '::error title=release-bins StoreTransportDisagreePresentMiss::the cache API holds an exact admitted entry for this key but the transport did not restore it exactly; refusing rather than rebuilding over a present entry'; exit 1); else if '[' "$LOOKUP_STANDING" '=' 'absent' ']' && '[' "$CACHE_HIT" '=' 'true' ']'; then ('echo' '::error title=release-bins StoreTransportDisagreeAbsentHit::the cache API holds no exact admitted entry for this key but the transport reported an exact hit; the store and its transport disagree'; exit 1); else if '[' "$LOOKUP_STANDING" '=' 'absent' ']' && '[' "$CACHE_HIT" '!=' 'true' ']'; then 'rm' '-rf' "$STAGE"; 'mkdir' '-p' "$STAGE"; 'echo' 'outcome=build' | 'tee' '-a' "$GITHUB_OUTPUT" > '/dev/null'; exit 0; else ('echo' '::error title=release-bins OutcomeInputsMissing::the lookup standing or the transport exactness is missing, so no outcome can be decided'; exit 1); fi; fi; fi; fi + '[' '-f' "$STAGE"'/release-bins.preimage' ']' || ('echo' '::error title=release-bins StoredEntryIncomplete::the stored entry is incomplete: release-bins.preimage is missing'; exit 1) + '[' '-f' "$STAGE"'/release-bins.manifest' ']' || ('echo' '::error title=release-bins StoredEntryIncomplete::the stored entry is incomplete: release-bins.manifest is missing'; exit 1) + '[' '-f' "$STAGE"'/release-bins.tgz' ']' || ('echo' '::error title=release-bins StoredEntryIncomplete::the stored entry is incomplete: release-bins.tgz is missing'; exit 1) + 'cmp' '-s' "$EXPECTED" "$STAGE"'/release-bins.preimage' || ('diff' "$EXPECTED" "$STAGE"'/release-bins.preimage' || ':' 'no-op'; ('echo' '::error title=release-bins StoredPreimageDiffers::the stored preimage differs from this run'\''s independently derived preimage'; exit 1)) + 'sha256sum' "$STAGE"'/release-bins.preimage' > "$RUNNER_TEMP"'/STORED_DIGEST.out' + STORED_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/STORED_DIGEST.out') + '[' 'release-bins-'"$STORED_DIGEST" '=' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins StoredPreimageKeyMismatch::the stored preimage does not hash to the probed key'; exit 1) + 'printf' '%s' 'claim_executor + gunbc + build_diagnostics + ' > "$RUNNER_TEMP"'/release-bins.roster' + 'tar' '-tzf' "$STAGE"'/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' + 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the stored pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) + 'cut' '-c' '67-' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/manifest-names.out' + 'cmp' '-s' "$RUNNER_TEMP"'/manifest-names.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins ManifestRosterMismatch::the stored manifest does not name exactly the gunbc.ci_release_bins roster in order'; exit 1) + if 'grep' '-qvE' '^[0-9a-f]{64} [A-Za-z0-9_]+$' "$STAGE"'/release-bins.manifest'; then ('echo' '::error title=release-bins ManifestMalformed::the stored manifest has a line that is not one well-formed sha256 record for a roster member'; exit 1); fi + 'rm' '-rf' "$STAGE"'/unpacked' + 'mkdir' '-p' "$STAGE"'/unpacked' + 'tar' '-xzf' "$STAGE"'/release-bins.tgz' '-C' "$STAGE"'/unpacked' '--no-same-owner' + 'printf' '%s' 'claim_executor:regular file:1 + gunbc:regular file:1 + ' > "$RUNNER_TEMP"'/member-types.expected' + ('cd' "$STAGE"'/unpacked' && 'stat' '-c' '%n:%F:%h' 'claim_executor' 'gunbc') > "$RUNNER_TEMP"'/member-types.out' + 'cmp' '-s' "$RUNNER_TEMP"'/member-types.out' "$RUNNER_TEMP"'/member-types.expected' || ('diff' "$RUNNER_TEMP"'/member-types.expected' "$RUNNER_TEMP"'/member-types.out' || ':' 'no-op'; ('echo' '::error title=release-bins MemberNotRegularFile::the stored pack has a member that is not a self-contained regular file (a link, special file or hard-link dependency; the report above names it)'; exit 1)) + ('cd' "$STAGE"'/unpacked' && 'find' 'claim_executor' 'gunbc' '-maxdepth' '0' '!' '-perm' '-u+x') > "$RUNNER_TEMP"'/member-nonexec.out' + '[' '!' '-s' "$RUNNER_TEMP"'/member-nonexec.out' ']' || ('cat' "$RUNNER_TEMP"'/member-nonexec.out'; ('echo' '::error title=release-bins MemberNotExecutable::the stored pack has a member that is not executable (listed above)'; exit 1)) + ('cd' "$STAGE"'/unpacked' && 'sha256sum' '--strict' '--quiet' '-c' "$STAGE"'/release-bins.manifest') || ('echo' '::error title=release-bins MemberDigestMismatch::the stored member bytes do not match the manifest under strict checking; refusing before any member is promoted or run'; exit 1) + 'mkdir' '-p' "$ROOT"'/target/release' + ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') + ('cd' "$STAGE"'/unpacked' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + 'rm' '-rf' "$STAGE"'/unpacked' + 'echo' 'release-bins: stored pack verified and promoted' + 'sha256sum' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/OUTPUT_DIGEST.out' + OUTPUT_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/OUTPUT_DIGEST.out') + 'echo' 'ReleaseBinsReuseReceipt: request='"$RELEASE_BINS_KEY"' output=sha256:'"$OUTPUT_DIGEST"' dispatch='"$GITHUB_RUN_ID"'/'"$GITHUB_RUN_ATTEMPT"' producer=['"$LOOKUP_ENTRY"'] built=no' + 'echo' 'outcome=reuse' | 'tee' '-a' "$GITHUB_OUTPUT" > '/dev/null' + env: + RELEASE_BINS_KEY: ${{ steps.release_bins_key.outputs.key }} + LOOKUP_STANDING: ${{ steps.release_bins_lookup.outputs.standing }} + LOOKUP_ENTRY: ${{ steps.release_bins_lookup.outputs.entry }} + CACHE_HIT: ${{ steps.release_bins_cache.outputs.cache-hit }} + timeout-minutes: 5 + - name: Cache Cargo + uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 + with: + path: |- + ${{ runner.temp }}/cargo/registry/index/ + ${{ runner.temp }}/cargo/registry/cache/ + ${{ runner.temp }}/cargo/git/db/ + target/ + key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} + restore-keys: | + cargo-ci-${{ runner.os }}-${{ runner.arch }}- + if: steps.release_bins_outcome.outputs.outcome != 'reuse' + - name: Build the compiler pair (keyed miss only) id: build_witness_fold run: |+ GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' @@ -334,7 +317,8 @@ jobs: if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi 'exit' "$GUNBC_FLOOR_EXIT" - - name: Pack the compiler pair candidate with its build log + if: steps.release_bins_outcome.outputs.outcome != 'reuse' + - name: Pack the compiler pair with its build log (build outcomes only) run: | set -e ROOT=$('git' 'rev-parse' '--show-toplevel') @@ -348,12 +332,30 @@ jobs: 'sha256sum' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/OUTPUT_DIGEST.out' OUTPUT_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/OUTPUT_DIGEST.out') 'echo' 'ReleaseBinsBuildReceipt: request='"$RELEASE_BINS_KEY"' output=sha256:'"$OUTPUT_DIGEST"' dispatch='"$GITHUB_RUN_ID"'/'"$GITHUB_RUN_ATTEMPT"' built=yes publish=requested-not-yet-persisted scope='"$GITHUB_REF" - + env: + RELEASE_BINS_KEY: ${{ steps.release_bins_key.outputs.key }} + if: steps.release_bins_outcome.outputs.outcome != 'reuse' + timeout-minutes: 5 + - name: Upload release-bins artifact + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: release-bins + path: |- + .release-bins-staging/release-bins.tgz + .release-bins-staging/release-bins.preimage + .release-bins-staging/release-bins.manifest + if-no-files-found: error + retention-days: 3 + compression-level: 0 + timeout-minutes: 10 + - name: Wrap the compiler pair as the merge-group candidate object + run: | set -e ROOT=$('git' 'rev-parse' '--show-toplevel') STAGE="$ROOT"'/.release-bins-staging' 'tar' '-cf' "$RUNNER_TEMP"'/pack.tar' '-C' "$STAGE" 'release-bins.preimage' 'release-bins.manifest' 'release-bins.tgz' - if: github.event_name == 'merge_group' && steps.build_witness_fold.outcome == 'success' + if: github.event_name == 'merge_group' + timeout-minutes: 5 - name: Upload the compiler pair candidate uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f with: @@ -362,7 +364,244 @@ jobs: if-no-files-found: error retention-days: 1 compression-level: 0 - if: github.event_name == 'merge_group' && steps.build_witness_fold.outcome == 'success' + if: github.event_name == 'merge_group' + - name: Require isolated toolchain homes + run: | + if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi + if: "!cancelled()" + env: + MALLOC_ARENA_MAX: "2" + emit-build: + runs-on: [self-hosted, linux, arm64] + needs: [seed] + timeout-minutes: 90 + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + permissions: + contents: read + steps: + - name: Checkout the commit this lane judges + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Isolate toolchain homes + run: |- + # dissolve-on: ci_toolchain_home_isolation_script -- orch-emitted foreign-executor prelude step wiping and setting HOME/CARGO_HOME/RUSTUP_HOME under RUNNER_TEMP so concurrent runner slots stop sharing one toolchain; leaf rm/echo strings remain until a typed per-job filesystem-and-environment effect lands on host_effect_apply (shell-to-intent Phase 2). This obligation covers THIS carrier and ci_isolate_toolchain_script, which share that terminal construction; ci_pin_rustup_default_script carries its own obligation because it does not + rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo" + echo "HOME=$RUNNER_TEMP" >> "$GITHUB_ENV" + echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" + echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" + - name: Install Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 + with: + components: rustfmt + cache: false + rustflags: -D warnings + - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) + run: |- + # dissolve-on: ci_pin_rustup_default_script -- orch-emitted foreign-executor step selecting a rustup default toolchain inside an isolated RUSTUP_HOME, which starts with none, and resolving the cargo binary that selection implies. The leaf rustup/command/echo strings remain until a typed TOOLCHAIN-SELECTION effect lands on host_effect_apply -- NOT the filesystem-and-environment effect ci_toolchain_home_isolation_script waits on, which is why this is a separate obligation: that effect landing alone would leave this carrier standing + rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" + if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi + if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi + echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" + - name: Download release-bins artifact + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + with: + name: release-bins + path: ${{ runner.temp }}/release-bins-download + timeout-minutes: 10 + - name: Verify the seed's compiler pair against its key and promote it into this checkout + id: build_witness_fold + run: | + set -e + ROOT=$('git' 'rev-parse' '--show-toplevel') + '[' '-n' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerKeyMissing::the build job published no release-bins key'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.preimage'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.manifest'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.tgz'; exit 1) + 'sha256sum' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' > "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out' + DOWNLOADED_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out') + '[' 'release-bins-'"$DOWNLOADED_DIGEST" '=' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerPreimageKeyMismatch::the downloaded preimage does not hash to the build job'\''s key'; exit 1) + OBJECT_FORMAT=$('git' '-C' "$ROOT" 'rev-parse' '--show-object-format') + SOURCE_TREE=$('git' '-C' "$ROOT" 'rev-parse' 'HEAD^{tree}') + '[' '-n' "$OBJECT_FORMAT" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input OBJECT_FORMAT read back empty, so the key would not name the build; refusing to key'; exit 1) + '[' '-n' "$SOURCE_TREE" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input SOURCE_TREE read back empty, so the key would not name the build; refusing to key'; exit 1) + SOURCE_CLOSURE='tree:'"$OBJECT_FORMAT"':'"$SOURCE_TREE" + 'grep' '-qxF' 'source_closure='"$SOURCE_CLOSURE" "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' || ('echo' '::error title=release-bins ConsumerSourceTreeMismatch::the pack was keyed on a different source tree than this job'\''s checkout'; exit 1) + 'printf' '%s' 'claim_executor + gunbc + build_diagnostics + ' > "$RUNNER_TEMP"'/release-bins.roster' + 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' + 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) + 'cut' '-c' '67-' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' > "$RUNNER_TEMP"'/manifest-names.out' + 'cmp' '-s' "$RUNNER_TEMP"'/manifest-names.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins ManifestRosterMismatch::the downloaded manifest does not name exactly the gunbc.ci_release_bins roster in order'; exit 1) + if 'grep' '-qvE' '^[0-9a-f]{64} [A-Za-z0-9_]+$' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest'; then ('echo' '::error title=release-bins ManifestMalformed::the downloaded manifest has a line that is not one well-formed sha256 record for a roster member'; exit 1); fi + 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' + 'mkdir' '-p' "$RUNNER_TEMP"'/release-bins-check' + 'tar' '-xzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' '-C' "$RUNNER_TEMP"'/release-bins-check' '--no-same-owner' + 'printf' '%s' 'claim_executor:regular file:1 + gunbc:regular file:1 + ' > "$RUNNER_TEMP"'/member-types.expected' + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'stat' '-c' '%n:%F:%h' 'claim_executor' 'gunbc') > "$RUNNER_TEMP"'/member-types.out' + 'cmp' '-s' "$RUNNER_TEMP"'/member-types.out' "$RUNNER_TEMP"'/member-types.expected' || ('diff' "$RUNNER_TEMP"'/member-types.expected' "$RUNNER_TEMP"'/member-types.out' || ':' 'no-op'; ('echo' '::error title=release-bins MemberNotRegularFile::the downloaded pack has a member that is not a self-contained regular file (a link, special file or hard-link dependency; the report above names it)'; exit 1)) + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'find' 'claim_executor' 'gunbc' '-maxdepth' '0' '!' '-perm' '-u+x') > "$RUNNER_TEMP"'/member-nonexec.out' + '[' '!' '-s' "$RUNNER_TEMP"'/member-nonexec.out' ']' || ('cat' "$RUNNER_TEMP"'/member-nonexec.out'; ('echo' '::error title=release-bins MemberNotExecutable::the downloaded pack has a member that is not executable (listed above)'; exit 1)) + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'sha256sum' '--strict' '--quiet' '-c' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest') || ('echo' '::error title=release-bins MemberDigestMismatch::the downloaded member bytes do not match the manifest under strict checking; refusing before any member is promoted or run'; exit 1) + 'mkdir' '-p' "$ROOT"'/target/release' + ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' + "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' + env: + RELEASE_BINS_KEY: ${{ needs.seed.outputs.release_bins_key }} + timeout-minutes: 5 + - name: Require isolated toolchain homes + run: | + if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi + if: "!cancelled()" + - name: emit and build //gunbc/instruments:self-host + id: emit_build_self_host + run: |+ + GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' + 'set' '+e' + 'set' '-o' 'pipefail' + ('sh' '-e' '-c' ''\''target/release/gunbc'\'' '\''test'\'' '\''//gunbc/instruments:self-host'\''') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" + GUNBC_FLOOR_EXIT="$?" + GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' + GUNBC_FLOOR_CLASS='structural' + GUNBC_FLOOR_SIGNATURE='' + if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi + if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi + 'exit' "$GUNBC_FLOOR_EXIT" + + - name: emit and build //gunbc/instruments:v2-native-cli + id: emit_build_v2_native_cli + run: |+ + GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' + 'set' '+e' + 'set' '-o' 'pipefail' + ('sh' '-e' '-c' ''\''target/release/gunbc'\'' '\''test'\'' '\''//gunbc/instruments:v2-native-cli'\''') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" + GUNBC_FLOOR_EXIT="$?" + GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' + GUNBC_FLOOR_CLASS='structural' + GUNBC_FLOOR_SIGNATURE='' + if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi + if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi + 'exit' "$GUNBC_FLOOR_EXIT" + + - name: Why this lane is red, and what would be a real finding + run: |- + printf '%s\n' 'emit-build is a REQUIRED lane: a red here BLOCKS your merge, through the required context `witnesses`.' + printf '%s\n' 'No standing break is declared, so a red here is a REAL FINDING -- most likely yours.' + if: failure() + env: + MALLOC_ARENA_MAX: "2" + floor: + runs-on: [self-hosted, linux, arm64] + needs: [seed] + timeout-minutes: 90 + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + permissions: + contents: read + steps: + - name: Checkout the commit this lane judges + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Isolate toolchain homes + run: |- + # dissolve-on: ci_toolchain_home_isolation_script -- orch-emitted foreign-executor prelude step wiping and setting HOME/CARGO_HOME/RUSTUP_HOME under RUNNER_TEMP so concurrent runner slots stop sharing one toolchain; leaf rm/echo strings remain until a typed per-job filesystem-and-environment effect lands on host_effect_apply (shell-to-intent Phase 2). This obligation covers THIS carrier and ci_isolate_toolchain_script, which share that terminal construction; ci_pin_rustup_default_script carries its own obligation because it does not + rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo" + echo "HOME=$RUNNER_TEMP" >> "$GITHUB_ENV" + echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" + echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" + - name: Install Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 + with: + components: rustfmt + cache: false + rustflags: -D warnings + - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) + run: |- + # dissolve-on: ci_pin_rustup_default_script -- orch-emitted foreign-executor step selecting a rustup default toolchain inside an isolated RUSTUP_HOME, which starts with none, and resolving the cargo binary that selection implies. The leaf rustup/command/echo strings remain until a typed TOOLCHAIN-SELECTION effect lands on host_effect_apply -- NOT the filesystem-and-environment effect ci_toolchain_home_isolation_script waits on, which is why this is a separate obligation: that effect landing alone would leave this carrier standing + rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" + if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi + if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi + echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" + - name: Download release-bins artifact + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + with: + name: release-bins + path: ${{ runner.temp }}/release-bins-download + timeout-minutes: 10 + - name: Verify the seed's compiler pair against its key and promote it into this checkout + id: build_witness_fold + run: | + set -e + ROOT=$('git' 'rev-parse' '--show-toplevel') + '[' '-n' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerKeyMissing::the build job published no release-bins key'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.preimage'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.manifest'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.tgz'; exit 1) + 'sha256sum' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' > "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out' + DOWNLOADED_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out') + '[' 'release-bins-'"$DOWNLOADED_DIGEST" '=' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerPreimageKeyMismatch::the downloaded preimage does not hash to the build job'\''s key'; exit 1) + OBJECT_FORMAT=$('git' '-C' "$ROOT" 'rev-parse' '--show-object-format') + SOURCE_TREE=$('git' '-C' "$ROOT" 'rev-parse' 'HEAD^{tree}') + '[' '-n' "$OBJECT_FORMAT" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input OBJECT_FORMAT read back empty, so the key would not name the build; refusing to key'; exit 1) + '[' '-n' "$SOURCE_TREE" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input SOURCE_TREE read back empty, so the key would not name the build; refusing to key'; exit 1) + SOURCE_CLOSURE='tree:'"$OBJECT_FORMAT"':'"$SOURCE_TREE" + 'grep' '-qxF' 'source_closure='"$SOURCE_CLOSURE" "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' || ('echo' '::error title=release-bins ConsumerSourceTreeMismatch::the pack was keyed on a different source tree than this job'\''s checkout'; exit 1) + 'printf' '%s' 'claim_executor + gunbc + build_diagnostics + ' > "$RUNNER_TEMP"'/release-bins.roster' + 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' + 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) + 'cut' '-c' '67-' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' > "$RUNNER_TEMP"'/manifest-names.out' + 'cmp' '-s' "$RUNNER_TEMP"'/manifest-names.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins ManifestRosterMismatch::the downloaded manifest does not name exactly the gunbc.ci_release_bins roster in order'; exit 1) + if 'grep' '-qvE' '^[0-9a-f]{64} [A-Za-z0-9_]+$' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest'; then ('echo' '::error title=release-bins ManifestMalformed::the downloaded manifest has a line that is not one well-formed sha256 record for a roster member'; exit 1); fi + 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' + 'mkdir' '-p' "$RUNNER_TEMP"'/release-bins-check' + 'tar' '-xzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' '-C' "$RUNNER_TEMP"'/release-bins-check' '--no-same-owner' + 'printf' '%s' 'claim_executor:regular file:1 + gunbc:regular file:1 + ' > "$RUNNER_TEMP"'/member-types.expected' + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'stat' '-c' '%n:%F:%h' 'claim_executor' 'gunbc') > "$RUNNER_TEMP"'/member-types.out' + 'cmp' '-s' "$RUNNER_TEMP"'/member-types.out' "$RUNNER_TEMP"'/member-types.expected' || ('diff' "$RUNNER_TEMP"'/member-types.expected' "$RUNNER_TEMP"'/member-types.out' || ':' 'no-op'; ('echo' '::error title=release-bins MemberNotRegularFile::the downloaded pack has a member that is not a self-contained regular file (a link, special file or hard-link dependency; the report above names it)'; exit 1)) + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'find' 'claim_executor' 'gunbc' '-maxdepth' '0' '!' '-perm' '-u+x') > "$RUNNER_TEMP"'/member-nonexec.out' + '[' '!' '-s' "$RUNNER_TEMP"'/member-nonexec.out' ']' || ('cat' "$RUNNER_TEMP"'/member-nonexec.out'; ('echo' '::error title=release-bins MemberNotExecutable::the downloaded pack has a member that is not executable (listed above)'; exit 1)) + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'sha256sum' '--strict' '--quiet' '-c' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest') || ('echo' '::error title=release-bins MemberDigestMismatch::the downloaded member bytes do not match the manifest under strict checking; refusing before any member is promoted or run'; exit 1) + 'mkdir' '-p' "$ROOT"'/target/release' + ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' + "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' + env: + RELEASE_BINS_KEY: ${{ needs.seed.outputs.release_bins_key }} + timeout-minutes: 5 - name: Require isolated toolchain homes run: | if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi @@ -475,6 +714,7 @@ jobs: GUNBC_REQUIRED_FLOOR_CROSS_CLAIM_DEMAND: required_floor_cross_claim_demand.tsv generated: runs-on: [self-hosted, linux, arm64] + needs: [seed] timeout-minutes: 90 if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository permissions: @@ -506,30 +746,58 @@ jobs: if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" - - name: Build the compiler and the witness executor + - name: Download release-bins artifact + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + with: + name: release-bins + path: ${{ runner.temp }}/release-bins-download + timeout-minutes: 10 + - name: Verify the seed's compiler pair against its key and promote it into this checkout id: build_witness_fold - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'cargo build --release -p v1-compiler --bin claim_executor --bin gunbc') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" - GUNBC_FLOOR_EXIT="$?" - GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' - GUNBC_FLOOR_CLASS='structural' - GUNBC_FLOOR_SIGNATURE='' - if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi - if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi - if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi - 'exit' "$GUNBC_FLOOR_EXIT" - + run: | + set -e + ROOT=$('git' 'rev-parse' '--show-toplevel') + '[' '-n' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerKeyMissing::the build job published no release-bins key'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.preimage'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.manifest'; exit 1) + '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.tgz'; exit 1) + 'sha256sum' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' > "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out' + DOWNLOADED_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out') + '[' 'release-bins-'"$DOWNLOADED_DIGEST" '=' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerPreimageKeyMismatch::the downloaded preimage does not hash to the build job'\''s key'; exit 1) + OBJECT_FORMAT=$('git' '-C' "$ROOT" 'rev-parse' '--show-object-format') + SOURCE_TREE=$('git' '-C' "$ROOT" 'rev-parse' 'HEAD^{tree}') + '[' '-n' "$OBJECT_FORMAT" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input OBJECT_FORMAT read back empty, so the key would not name the build; refusing to key'; exit 1) + '[' '-n' "$SOURCE_TREE" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input SOURCE_TREE read back empty, so the key would not name the build; refusing to key'; exit 1) + SOURCE_CLOSURE='tree:'"$OBJECT_FORMAT"':'"$SOURCE_TREE" + 'grep' '-qxF' 'source_closure='"$SOURCE_CLOSURE" "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' || ('echo' '::error title=release-bins ConsumerSourceTreeMismatch::the pack was keyed on a different source tree than this job'\''s checkout'; exit 1) + 'printf' '%s' 'claim_executor + gunbc + build_diagnostics + ' > "$RUNNER_TEMP"'/release-bins.roster' + 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' + 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) + 'cut' '-c' '67-' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' > "$RUNNER_TEMP"'/manifest-names.out' + 'cmp' '-s' "$RUNNER_TEMP"'/manifest-names.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins ManifestRosterMismatch::the downloaded manifest does not name exactly the gunbc.ci_release_bins roster in order'; exit 1) + if 'grep' '-qvE' '^[0-9a-f]{64} [A-Za-z0-9_]+$' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest'; then ('echo' '::error title=release-bins ManifestMalformed::the downloaded manifest has a line that is not one well-formed sha256 record for a roster member'; exit 1); fi + 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' + 'mkdir' '-p' "$RUNNER_TEMP"'/release-bins-check' + 'tar' '-xzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' '-C' "$RUNNER_TEMP"'/release-bins-check' '--no-same-owner' + 'printf' '%s' 'claim_executor:regular file:1 + gunbc:regular file:1 + ' > "$RUNNER_TEMP"'/member-types.expected' + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'stat' '-c' '%n:%F:%h' 'claim_executor' 'gunbc') > "$RUNNER_TEMP"'/member-types.out' + 'cmp' '-s' "$RUNNER_TEMP"'/member-types.out' "$RUNNER_TEMP"'/member-types.expected' || ('diff' "$RUNNER_TEMP"'/member-types.expected' "$RUNNER_TEMP"'/member-types.out' || ':' 'no-op'; ('echo' '::error title=release-bins MemberNotRegularFile::the downloaded pack has a member that is not a self-contained regular file (a link, special file or hard-link dependency; the report above names it)'; exit 1)) + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'find' 'claim_executor' 'gunbc' '-maxdepth' '0' '!' '-perm' '-u+x') > "$RUNNER_TEMP"'/member-nonexec.out' + '[' '!' '-s' "$RUNNER_TEMP"'/member-nonexec.out' ']' || ('cat' "$RUNNER_TEMP"'/member-nonexec.out'; ('echo' '::error title=release-bins MemberNotExecutable::the downloaded pack has a member that is not executable (listed above)'; exit 1)) + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'sha256sum' '--strict' '--quiet' '-c' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest') || ('echo' '::error title=release-bins MemberDigestMismatch::the downloaded member bytes do not match the manifest under strict checking; refusing before any member is promoted or run'; exit 1) + 'mkdir' '-p' "$ROOT"'/target/release' + ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' + "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' + env: + RELEASE_BINS_KEY: ${{ needs.seed.outputs.release_bins_key }} + timeout-minutes: 5 - name: Require isolated toolchain homes run: | if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi @@ -629,7 +897,7 @@ jobs: MALLOC_ARENA_MAX: "2" witnesses: runs-on: [self-hosted, linux, arm64] - needs: [emit-build, floor, generated] + needs: [seed, emit-build, floor, generated] timeout-minutes: 5 if: always() permissions: @@ -637,12 +905,14 @@ jobs: steps: - name: Every required lane must have succeeded run: |- - echo "required lanes: emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED (same_repo=$SAME_REPO)" - if [ "$EMIT_BUILD" = failure ] || [ "$FLOOR" = failure ] || [ "$GENERATED" = failure ]; then echo "::error::a required lane concluded failure (emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED) - open that job's log" >&2; exit 1; fi + echo "required lanes: seed=$SEED emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED (same_repo=$SAME_REPO)" + if [ "$SEED" = failure ] || [ "$EMIT_BUILD" = failure ] || [ "$FLOOR" = failure ] || [ "$GENERATED" = failure ]; then echo "::error::a required lane concluded failure (seed=$SEED emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED) - open that job's log" >&2; exit 1; fi + if [ "$SEED" != success ]; then if [ "$SAME_REPO" = false ] && [ "$SEED" = skipped ]; then echo "::notice::the seed lane is skipped for a fork pull request: no compiler pair was built or restored for this head"; else echo "::error::the seed lane produced no success (seed=$SEED), so this head carries no verdict from that lane"; exit 1; fi; fi if [ "$EMIT_BUILD" != success ]; then if [ "$SAME_REPO" = false ] && [ "$EMIT_BUILD" = skipped ]; then echo "::notice::the emit-build lane is skipped for a fork pull request: no self-host or native-CLI emission verdict for this head"; else echo "::error::the emit-build lane produced no success (emit-build=$EMIT_BUILD), so this head carries no verdict from that lane"; exit 1; fi; fi if [ "$FLOOR" != success ]; then if [ "$SAME_REPO" = false ] && [ "$FLOOR" = skipped ]; then echo "::notice::the fleet lane is skipped for a fork pull request: no witnesses fold and no generated-artifact verdict for this head"; else echo "::error::the fleet lane produced no success (floor=$FLOOR), so this head carries no verdict from that lane"; exit 1; fi; fi if [ "$GENERATED" != success ]; then if [ "$SAME_REPO" = false ] && [ "$GENERATED" = skipped ]; then echo "::notice::the generated-artifact lane is skipped for a fork pull request: no generated-artifact verdict for this head"; else echo "::error::the generated-artifact lane produced no success (generated=$GENERATED), so this head carries no verdict from that lane"; exit 1; fi; fi env: + SEED: ${{ needs.seed.result }} EMIT_BUILD: ${{ needs['emit-build'].result }} FLOOR: ${{ needs.floor.result }} GENERATED: ${{ needs.generated.result }} diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index bc7a91c95a7..83711101204 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -1162,13 +1162,13 @@ data compiler_gate_seed_job_id: String = "seed" data compiler_gate_seed_build_step_name: String = "Build the compiler pair (keyed miss only)" data compiler_gate_pair_restore_step_name: String = "Verify the seed's compiler pair against its key and promote it into this checkout" -fn compiler_gate_seed_run_step(name: String, id: String?, script: String, env: List?, condition: String?, timeout: Int?) -> Step { +fn compiler_gate_seed_run_step(name: String, id: String?, script: String, env: List, condition: String?, timeout: Int?) -> Step { RunStep { name: Present { value: name }, id: id, run: script, shell: none, - env: env, + env: if length(xs: env) == 0 { none } else { Present { value: env } }, working_directory: none, if_condition: condition, continue_on_error: none, @@ -1185,7 +1185,7 @@ fn compiler_gate_pair_restore_step() -> Step { name: compiler_gate_pair_restore_step_name, id: Present { value: "build_witness_fold" }, script: release_bins_consumer_key_check_script_for(product: compiler_pair_release_product()), - env: Present { value: [kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: compiler_gate_seed_key_expression()))] }, + env: [kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: compiler_gate_seed_key_expression()))], condition: none, timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, ) @@ -1198,7 +1198,7 @@ fn compiler_gate_seed_bound_steps() -> List { name: "Key the compiler pair (stamps the build with the materialized tree)", id: Present { value: release_bins_key_step_id }, script: release_bins_key_script_for(product: compiler_pair_release_product()), - env: none, + env: [], condition: none, timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, ), @@ -1220,14 +1220,12 @@ fn compiler_gate_seed_bound_steps() -> List { name: "Decide the compiler pair outcome; verify a stored pack completely before any restored binary runs", id: Present { value: release_bins_outcome_step_id }, script: release_bins_outcome_script_for(product: compiler_pair_release_product()), - env: Present { - value: [ - kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: release_bins_key_expression())), - kv(key: "LOOKUP_STANDING", value: yaml_string(s: release_bins_lookup_standing_expression())), - kv(key: "LOOKUP_ENTRY", value: yaml_string(s: release_bins_lookup_entry_expression())), - kv(key: "CACHE_HIT", value: yaml_string(s: release_bins_cache_hit_expression())), - ] - }, + env: [ + kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: release_bins_key_expression())), + kv(key: "LOOKUP_STANDING", value: yaml_string(s: release_bins_lookup_standing_expression())), + kv(key: "LOOKUP_ENTRY", value: yaml_string(s: release_bins_lookup_entry_expression())), + kv(key: "CACHE_HIT", value: yaml_string(s: release_bins_cache_hit_expression())), + ], condition: none, timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, ), @@ -1244,7 +1242,7 @@ fn compiler_gate_seed_bound_steps() -> List { name: compiler_gate_seed_build_step_name, id: Present { value: "build_witness_fold" }, script: concat(wrap_command_with_floor_attempt_receipt(command: compiler_pair_release_product().build_line), "\n"), - env: none, + env: [], condition: Present { value: release_bins_build_condition() }, timeout: none, ), @@ -1256,7 +1254,7 @@ fn compiler_gate_seed_bound_steps() -> List { name: "Pack the compiler pair with its build log (build outcomes only)", id: none, script: release_bins_pack_manifest_script_for(product: compiler_pair_release_product()), - env: Present { value: [kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: release_bins_key_expression()))] }, + env: [kv(key: "RELEASE_BINS_KEY", value: yaml_string(s: release_bins_key_expression()))], condition: Present { value: release_bins_build_condition() }, timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, ), @@ -1273,7 +1271,7 @@ fn compiler_gate_seed_bound_steps() -> List { name: "Wrap the compiler pair as the merge-group candidate object", id: none, script: release_bins_pack_object_script(), - env: none, + env: [], condition: Present { value: compiler_gate_candidate_condition() }, timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, ), From fdeead9e4f11a47fe418cc6fd4ad6b9f4ecacc38 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 22:35:16 +0000 Subject: [PATCH 3/7] R1: emit-build builds its own gunbc (gunbc test freshness guard cannot judge a restored binary) Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/witnesses.yml | 75 ++++++-------------- dag/gunbc/witness/compiler_gate_workflow.dag | 26 ++++++- 2 files changed, 47 insertions(+), 54 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index f14e953e20f..e86853d249d 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -373,7 +373,6 @@ jobs: MALLOC_ARENA_MAX: "2" emit-build: runs-on: [self-hosted, linux, arm64] - needs: [seed] timeout-minutes: 90 if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository permissions: @@ -405,58 +404,30 @@ jobs: if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" - - name: Download release-bins artifact - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 - with: - name: release-bins - path: ${{ runner.temp }}/release-bins-download - timeout-minutes: 10 - - name: Verify the seed's compiler pair against its key and promote it into this checkout + - name: Build the compiler id: build_witness_fold - run: | - set -e - ROOT=$('git' 'rev-parse' '--show-toplevel') - '[' '-n' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerKeyMissing::the build job published no release-bins key'; exit 1) - '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.preimage'; exit 1) - '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.manifest'; exit 1) - '[' '-f' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' ']' || ('echo' '::error title=release-bins ConsumerFileMissing::the release-bins artifact is missing release-bins.tgz'; exit 1) - 'sha256sum' "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' > "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out' - DOWNLOADED_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/DOWNLOADED_DIGEST.out') - '[' 'release-bins-'"$DOWNLOADED_DIGEST" '=' "$RELEASE_BINS_KEY" ']' || ('echo' '::error title=release-bins ConsumerPreimageKeyMismatch::the downloaded preimage does not hash to the build job'\''s key'; exit 1) - OBJECT_FORMAT=$('git' '-C' "$ROOT" 'rev-parse' '--show-object-format') - SOURCE_TREE=$('git' '-C' "$ROOT" 'rev-parse' 'HEAD^{tree}') - '[' '-n' "$OBJECT_FORMAT" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input OBJECT_FORMAT read back empty, so the key would not name the build; refusing to key'; exit 1) - '[' '-n' "$SOURCE_TREE" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input SOURCE_TREE read back empty, so the key would not name the build; refusing to key'; exit 1) - SOURCE_CLOSURE='tree:'"$OBJECT_FORMAT"':'"$SOURCE_TREE" - 'grep' '-qxF' 'source_closure='"$SOURCE_CLOSURE" "$RUNNER_TEMP"'/release-bins-download/release-bins.preimage' || ('echo' '::error title=release-bins ConsumerSourceTreeMismatch::the pack was keyed on a different source tree than this job'\''s checkout'; exit 1) - 'printf' '%s' 'claim_executor - gunbc - build_diagnostics - ' > "$RUNNER_TEMP"'/release-bins.roster' - 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' - 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) - 'cut' '-c' '67-' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest' > "$RUNNER_TEMP"'/manifest-names.out' - 'cmp' '-s' "$RUNNER_TEMP"'/manifest-names.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins ManifestRosterMismatch::the downloaded manifest does not name exactly the gunbc.ci_release_bins roster in order'; exit 1) - if 'grep' '-qvE' '^[0-9a-f]{64} [A-Za-z0-9_]+$' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest'; then ('echo' '::error title=release-bins ManifestMalformed::the downloaded manifest has a line that is not one well-formed sha256 record for a roster member'; exit 1); fi - 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' - 'mkdir' '-p' "$RUNNER_TEMP"'/release-bins-check' - 'tar' '-xzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' '-C' "$RUNNER_TEMP"'/release-bins-check' '--no-same-owner' - 'printf' '%s' 'claim_executor:regular file:1 - gunbc:regular file:1 - ' > "$RUNNER_TEMP"'/member-types.expected' - ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'stat' '-c' '%n:%F:%h' 'claim_executor' 'gunbc') > "$RUNNER_TEMP"'/member-types.out' - 'cmp' '-s' "$RUNNER_TEMP"'/member-types.out' "$RUNNER_TEMP"'/member-types.expected' || ('diff' "$RUNNER_TEMP"'/member-types.expected' "$RUNNER_TEMP"'/member-types.out' || ':' 'no-op'; ('echo' '::error title=release-bins MemberNotRegularFile::the downloaded pack has a member that is not a self-contained regular file (a link, special file or hard-link dependency; the report above names it)'; exit 1)) - ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'find' 'claim_executor' 'gunbc' '-maxdepth' '0' '!' '-perm' '-u+x') > "$RUNNER_TEMP"'/member-nonexec.out' - '[' '!' '-s' "$RUNNER_TEMP"'/member-nonexec.out' ']' || ('cat' "$RUNNER_TEMP"'/member-nonexec.out'; ('echo' '::error title=release-bins MemberNotExecutable::the downloaded pack has a member that is not executable (listed above)'; exit 1)) - ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'sha256sum' '--strict' '--quiet' '-c' "$RUNNER_TEMP"'/release-bins-download/release-bins.manifest') || ('echo' '::error title=release-bins MemberDigestMismatch::the downloaded member bytes do not match the manifest under strict checking; refusing before any member is promoted or run'; exit 1) - 'mkdir' '-p' "$ROOT"'/target/release' - ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') - ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') - 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' - "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' - env: - RELEASE_BINS_KEY: ${{ needs.seed.outputs.release_bins_key }} - timeout-minutes: 5 + run: |+ + GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' + 'set' '+e' + 'set' '-o' 'pipefail' + ('sh' '-e' '-c' 'cargo build --release -p v1-compiler --bin gunbc') 2>&1 | 'tee' "$GUNBC_FLOOR_LOG" + GUNBC_FLOOR_EXIT="$?" + GUNBC_FLOOR_RECEIPT='gunbc-floor-outcome.txt' + GUNBC_FLOOR_CLASS='structural' + GUNBC_FLOOR_SIGNATURE='' + if '[' "$GUNBC_FLOOR_EXIT" '-eq' '126' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandInvokedCannotExecute'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '127' ']'; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='CommandNotFound'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' "$GUNBC_FLOOR_EXIT" '-eq' '0' ']'; then GUNBC_FLOOR_CLASS='none'; GUNBC_FLOOR_SIGNATURE=''; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'MemoryStallRefusedPageThrash' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='MemoryStallRefusedPageThrash'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'The runner has received a shutdown signal' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='RunnerLost'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: error: failed to execute compile' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFailedToExecuteCompile'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' '(exit status: 254)' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheRustcWrapperExit254'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'sccache: encountered fatal error' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='SccacheFatalError'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'failed to spawn' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ProcessSpawnFailure'; fi + if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' && '[' '-f' "$GUNBC_FLOOR_LOG" ']' && 'grep' '-q' 'Resource temporarily unavailable' "$GUNBC_FLOOR_LOG"; then GUNBC_FLOOR_CLASS='infra'; GUNBC_FLOOR_SIGNATURE='ResourceTemporarilyUnavailable'; fi + if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi + 'exit' "$GUNBC_FLOOR_EXIT" + - name: Require isolated toolchain homes run: | if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index 83711101204..065607498df 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -1094,9 +1094,31 @@ fn compiler_gate_emit_build_subject_bound_steps() -> List // THE PRELUDE IS THE FLEET PRELUDE over `gunbc` alone, because the subject steps need exactly what it // provides on the fleet: the judged checkout, an isolated toolchain home (and the guard requiring // it), a pinned default, and the `gunbc` binary the instruments are invoked through. +// EMIT-BUILD BUILDS ITS OWN `gunbc` AND DOES NOT RESTORE THE SEED PAIR. `gunbc test` refuses a binary +// whose cargo dep-info it cannot read inside this checkout (BinaryFreshnessUndecided, #13007): a +// restored pair carries no dep-info for this worktree, so the freshness guard rightly cannot judge +// it. Until that guard can judge a restored binary, this lane keeps the one build it always paid. +fn compiler_gate_emit_build_prelude_bound_steps() -> List { + concat(compiler_gate_fleet_head_bound_steps(), [ + CompilerGateBoundStep { + step: witness_floor_build_step( + step_name: compiler_gate_build_step_name, + build_script: repo_self_build_command(bins: compiler_gate_required_bins), + ), + role: consumes_only(capabilities: [CargoCapability]), + step_name: compiler_gate_build_step_name, + }, + CompilerGateBoundStep { + step: witness_toolchain_homes_isolated_guard_step(), + role: capability_neutral, + step_name: "Require isolated toolchain homes", + }, + ]) +} + fn compiler_gate_emit_build_bound_steps() -> List { concat( - concat(compiler_gate_fleet_prelude_bound_steps(), compiler_gate_emit_build_subject_bound_steps()), + concat(compiler_gate_emit_build_prelude_bound_steps(), compiler_gate_emit_build_subject_bound_steps()), [CompilerGateBoundStep { step: compiler_gate_emit_build_notice_step(), role: capability_neutral, @@ -1123,7 +1145,7 @@ fn compiler_gate_emit_build_job() -> Job { runner: gunbc_ci_selected_runner_spec(), environment: none, steps: list_map(xs: compiler_gate_emit_build_bound_steps(), f: fn(b) { b.step }), - needs: [compiler_gate_seed_job_id], + needs: [], env: Present { value: [ kv(key: malloc_arena_max_env_key, value: yaml_string(s: malloc_arena_max_value)) From eadef6145767d3a10b2bffa36835ba21c5b34c07 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 6 Oct 2026 22:50:59 +0000 Subject: [PATCH 4/7] R1: correct seed-lane prose (candidate producer, roster, citation) Co-Authored-By: Claude Sonnet 5.5 --- dag/gunbc/witness/compiler_gate_workflow.dag | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index 065607498df..ad74101a6ce 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -2,7 +2,7 @@ module gunbc.compiler_gate_workflow // NO LANE THE REQUIRED CONTEXT READS RUNS ON A HOSTED RUNNER (operator ruling 2026-09-28). The -// lanes the `witnesses` aggregate reads -- `emit-build`, `floor` and `generated` -- run on the fleet, +// lanes the `witnesses` aggregate reads -- `seed`, `emit-build`, `floor` and `generated` -- run on the fleet, // and so does the aggregate. // // THE HOSTED UNIT-TEST LANE WAS DELETED (operator ruling 2026-09-29). `rust-unit-tests` ran @@ -21,8 +21,8 @@ module gunbc.compiler_gate_workflow // jobs API's created_at and started_at rather than trusted here: merge-queue runs 36478070174 and // 36464765845, where every hosted job waited minutes for a slot -- the aggregate, whose one step // runs in seconds, waited 9 and 21 -- while the fleet lanes of the same runs started within -// seconds. So `compiler` is deleted: its one step built `gunbc`, which the fleet prelude of both -// `floor` and `generated` already builds from the same tree (DESIGN section 2). The lint became a +// seconds. So `compiler` is deleted: its one step built `gunbc`, which the `seed` job now builds or restores once +// for `floor` and `generated` (`emit-build` still builds its own, see the seed section) (DESIGN section 2). The lint became a // step on `generated` (compiler_gate_lint_step), and the aggregate moved to the fleet. // compiler_gate_merge_path_runs_on_the_fleet refuses emission of a workflow in which a blocking // lane or the aggregate runs hosted. @@ -891,15 +891,15 @@ fn compiler_gate_floor_scripts_are_renderable() -> Bool { // ── THE MERGE QUEUE PRODUCES THE COMPILER PAIR CANDIDATE ──────────────────────────────────────── // -// The floor already builds claim_executor and gunbc from the tree that will land. On a merge group -// (the composed revision that becomes main), it also keys that build as gunbc.fleet_release_bins_key -// compiler_pair_release_product BEFORE building -- so the build is stamped with the materialized tree, -// as the preimage says -- then packs the two binaries with the build log the build step just wrote, -// wraps the pack as one object and uploads it as a short-lived workflow artifact. The floor holds no -// cache credential: it produces a CANDIDATE, and fleet-desired, after the revision lands, verifies it -// and is the only canonical publisher. A pull-request run produces nothing (nothing would consume it). -// A refusal here is the floor's: an environment the product's preimage cannot honestly describe -// stops the line rather than yielding an unprovenanced candidate. +// The `seed` job builds or restores claim_executor and gunbc for the tree that will land. On a +// merge group (the composed revision that becomes main) it also packs the two binaries with the build +// log into one object and uploads it as a short-lived workflow artifact; the key was derived by +// gunbc.fleet_release_bins_key compiler_pair_release_product BEFORE any build, so the pack is stamped +// with the materialized tree, as the preimage says. The seed holds no cache credential: it produces +// a CANDIDATE, and fleet-desired, after the revision lands, verifies it and is the only canonical +// publisher. A pull-request run produces no candidate (nothing would consume it). A refusal here is +// the seed's: an environment the product's preimage cannot honestly describe stops the line rather +// than yielding an unprovenanced candidate. fn compiler_gate_candidate_condition() -> String { "github.event_name == 'merge_group'" } @@ -1163,7 +1163,7 @@ fn compiler_gate_emit_build_job() -> Job { // ═══════════════════════════════════════════════════════════════════════════ // THE SEED LANE: THE COMPILER PAIR IS BUILT OR RESTORED ONCE PER RUN. // -// OPERATOR DIRECTION 2026-10-06 (CI programme item 1, rows R1/R2 of docs/plans/ci-end-to-end-accounting.md): +// OPERATOR DIRECTION 2026-10-06 (CI programme item 1, rows R1/R2; operator authorized changing the required job roster for this programme): // the three fleet lanes each built the same two binaries from the same tree, so a run paid the // release build three times on three runners. The pair is one product keyed by // gunbc.fleet_release_bins_key compiler_pair_release_product -- the same keyed materialization From 09ab6df31b63314a775fb14aeb6d094828d5681e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 00:56:37 +0000 Subject: [PATCH 5/7] R1: typed checker-inputs sidecar pack member so the floor's checker-subject guard accepts a verified restored pair Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/fleet-converge.yml | 9 +- .github/workflows/fleet-desired.yml | 2 + .github/workflows/witnesses.yml | 16 ++- dag/gunbc/fleet/fleet_release_bins_key.dag | 44 +++++-- dag/gunbc/witness/compiler_gate_workflow.dag | 12 ++ src/v1/stage0/src/bin/claim_executor.rs | 22 ++++ src/v1/stage0/src/cli_run.rs | 1 + .../stage0/src/cli_run/checker_dependency.rs | 117 ++++++++++++++++-- 8 files changed, 204 insertions(+), 19 deletions(-) diff --git a/.github/workflows/fleet-converge.yml b/.github/workflows/fleet-converge.yml index 891570deec9..cbe7183cacb 100644 --- a/.github/workflows/fleet-converge.yml +++ b/.github/workflows/fleet-converge.yml @@ -382,6 +382,7 @@ jobs: 'printf' '%s' 'claim_executor gunbc build_diagnostics + claim_executor_checker_inputs ' > "$RUNNER_TEMP"'/release-bins.roster' 'tar' '-tzf' "$STAGE"'/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the stored pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) @@ -402,6 +403,7 @@ jobs: 'mkdir' '-p' "$ROOT"'/target/release' ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') ('cd' "$STAGE"'/unpacked' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + ('cd' "$STAGE"'/unpacked' && 'cp' '-p' 'claim_executor_checker_inputs' "$ROOT"'/target/release/') 'rm' '-rf' "$STAGE"'/unpacked' 'echo' 'release-bins: stored pack verified and promoted' 'sha256sum' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/OUTPUT_DIGEST.out' @@ -544,8 +546,9 @@ jobs: 'mkdir' '-p' "$STAGE" '[' '-s' "$ROOT"'/gunbc-floor-cmd.log' ']' || ('echo' '::error title=release-bins BuildDiagnosticsMissing::the product requires the build log gunbc-floor-cmd.log as a pack member and the build step left none; a pack without its diagnostics is incomplete, not smaller'; exit 1) 'cp' "$ROOT"'/gunbc-floor-cmd.log' "$ROOT"'/target/release/build_diagnostics' - ('cd' "$ROOT"'/target/release' && 'sha256sum' 'claim_executor' 'gunbc' 'build_diagnostics') > "$STAGE"'/release-bins.manifest' - 'tar' '-czf' "$STAGE"'/release-bins.tgz' '-C' "$ROOT"'/target/release' 'claim_executor' 'gunbc' 'build_diagnostics' + '[' '-s' "$ROOT"'/target/release/claim_executor_checker_inputs' ']' || ('echo' '::error title=release-bins SidecarMissing::the product requires the sidecar claim_executor_checker_inputs as a pack member and the producing job left none; a pack without it is incomplete, not smaller'; exit 1) + ('cd' "$ROOT"'/target/release' && 'sha256sum' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs') > "$STAGE"'/release-bins.manifest' + 'tar' '-czf' "$STAGE"'/release-bins.tgz' '-C' "$ROOT"'/target/release' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs' 'cp' "$RUNNER_TEMP"'/release-bins.preimage.expected' "$STAGE"'/release-bins.preimage' 'sha256sum' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/OUTPUT_DIGEST.out' OUTPUT_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/OUTPUT_DIGEST.out') @@ -627,6 +630,7 @@ jobs: 'printf' '%s' 'claim_executor gunbc build_diagnostics + claim_executor_checker_inputs ' > "$RUNNER_TEMP"'/release-bins.roster' 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) @@ -647,6 +651,7 @@ jobs: 'mkdir' '-p' "$ROOT"'/target/release' ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor_checker_inputs' "$ROOT"'/target/release/') 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' diff --git a/.github/workflows/fleet-desired.yml b/.github/workflows/fleet-desired.yml index 12ee3f523b1..f9ff7b0c00e 100644 --- a/.github/workflows/fleet-desired.yml +++ b/.github/workflows/fleet-desired.yml @@ -87,6 +87,7 @@ jobs: 'printf' '%s' 'claim_executor gunbc build_diagnostics + claim_executor_checker_inputs ' > "$RUNNER_TEMP"'/release-bins.roster' 'tar' '-tzf' "$RUNNER_TEMP"'/canonical-pack/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) @@ -107,6 +108,7 @@ jobs: 'mkdir' '-p' "$ROOT"'/target/release' ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') ('cd' "$RUNNER_TEMP"'/canonical-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + ('cd' "$RUNNER_TEMP"'/canonical-check' && 'cp' '-p' 'claim_executor_checker_inputs' "$ROOT"'/target/release/') 'rm' '-rf' "$RUNNER_TEMP"'/canonical-check' "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' 'echo' 'release-bins: canonical gunbc.compiler_pair_release_bins pack verified and promoted' diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index e86853d249d..28f9c573b32 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -249,6 +249,7 @@ jobs: 'printf' '%s' 'claim_executor gunbc build_diagnostics + claim_executor_checker_inputs ' > "$RUNNER_TEMP"'/release-bins.roster' 'tar' '-tzf' "$STAGE"'/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the stored pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) @@ -269,6 +270,7 @@ jobs: 'mkdir' '-p' "$ROOT"'/target/release' ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') ('cd' "$STAGE"'/unpacked' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + ('cd' "$STAGE"'/unpacked' && 'cp' '-p' 'claim_executor_checker_inputs' "$ROOT"'/target/release/') 'rm' '-rf' "$STAGE"'/unpacked' 'echo' 'release-bins: stored pack verified and promoted' 'sha256sum' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/OUTPUT_DIGEST.out' @@ -318,6 +320,11 @@ jobs: 'exit' "$GUNBC_FLOOR_EXIT" if: steps.release_bins_outcome.outputs.outcome != 'reuse' + - name: Record the checker inputs beside the pair (build outcomes only) + run: | + ./target/release/claim_executor --write-checker-inputs target/release/claim_executor_checker_inputs + if: steps.release_bins_outcome.outputs.outcome != 'reuse' + timeout-minutes: 5 - name: Pack the compiler pair with its build log (build outcomes only) run: | set -e @@ -326,8 +333,9 @@ jobs: 'mkdir' '-p' "$STAGE" '[' '-s' "$ROOT"'/gunbc-floor-cmd.log' ']' || ('echo' '::error title=release-bins BuildDiagnosticsMissing::the product requires the build log gunbc-floor-cmd.log as a pack member and the build step left none; a pack without its diagnostics is incomplete, not smaller'; exit 1) 'cp' "$ROOT"'/gunbc-floor-cmd.log' "$ROOT"'/target/release/build_diagnostics' - ('cd' "$ROOT"'/target/release' && 'sha256sum' 'claim_executor' 'gunbc' 'build_diagnostics') > "$STAGE"'/release-bins.manifest' - 'tar' '-czf' "$STAGE"'/release-bins.tgz' '-C' "$ROOT"'/target/release' 'claim_executor' 'gunbc' 'build_diagnostics' + '[' '-s' "$ROOT"'/target/release/claim_executor_checker_inputs' ']' || ('echo' '::error title=release-bins SidecarMissing::the product requires the sidecar claim_executor_checker_inputs as a pack member and the producing job left none; a pack without it is incomplete, not smaller'; exit 1) + ('cd' "$ROOT"'/target/release' && 'sha256sum' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs') > "$STAGE"'/release-bins.manifest' + 'tar' '-czf' "$STAGE"'/release-bins.tgz' '-C' "$ROOT"'/target/release' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs' 'cp' "$RUNNER_TEMP"'/release-bins.preimage.expected' "$STAGE"'/release-bins.preimage' 'sha256sum' "$STAGE"'/release-bins.manifest' > "$RUNNER_TEMP"'/OUTPUT_DIGEST.out' OUTPUT_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/OUTPUT_DIGEST.out') @@ -548,6 +556,7 @@ jobs: 'printf' '%s' 'claim_executor gunbc build_diagnostics + claim_executor_checker_inputs ' > "$RUNNER_TEMP"'/release-bins.roster' 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) @@ -568,6 +577,7 @@ jobs: 'mkdir' '-p' "$ROOT"'/target/release' ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor_checker_inputs' "$ROOT"'/target/release/') 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' env: @@ -744,6 +754,7 @@ jobs: 'printf' '%s' 'claim_executor gunbc build_diagnostics + claim_executor_checker_inputs ' > "$RUNNER_TEMP"'/release-bins.roster' 'tar' '-tzf' "$RUNNER_TEMP"'/release-bins-download/release-bins.tgz' > "$RUNNER_TEMP"'/pack-listing.out' 'cmp' '-s' "$RUNNER_TEMP"'/pack-listing.out' "$RUNNER_TEMP"'/release-bins.roster' || ('echo' '::error title=release-bins PackListingNotRoster::the downloaded pack'\''s archive listing is not exactly the roster (an extra, duplicate, directory or escaping entry)'; exit 1) @@ -764,6 +775,7 @@ jobs: 'mkdir' '-p' "$ROOT"'/target/release' ('cd' "$ROOT"'/target/release' && 'rm' '-f' 'claim_executor' 'gunbc') ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' "$ROOT"'/target/release/') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor_checker_inputs' "$ROOT"'/target/release/') 'rm' '-rf' "$RUNNER_TEMP"'/release-bins-check' "$ROOT"'/target/release/claim_executor' '--verify-build-artifacts' "$ROOT"'/target/release/claim_executor' "$ROOT"'/target/release/gunbc' env: diff --git a/dag/gunbc/fleet/fleet_release_bins_key.dag b/dag/gunbc/fleet/fleet_release_bins_key.dag index 02d45e4aa8f..1add12f15f6 100644 --- a/dag/gunbc/fleet/fleet_release_bins_key.dag +++ b/dag/gunbc/fleet/fleet_release_bins_key.dag @@ -161,6 +161,7 @@ type ReleaseBinsProduct { roster: List build_line: String diagnostics: ReleaseBinsDiagnostics + sidecars: List } // WHAT THE BUILD SAID, CARRIED AS A PACK MEMBER. A build whose warnings were dropped is not the build @@ -173,16 +174,27 @@ type ReleaseBinsDiagnostics = NoBuildDiagnostics | BuildLogMember { member: String, source: String } +// A SIDECAR IS A DATA MEMBER THE PRODUCING JOB WROTE INTO target/release BESIDE THE BINARIES, named by +// product and carried by the pack under the same digest, listing and verification as every member +// (it is not executable, so it is not in the roster the executable checks run over). A consumer +// promotes it only after the whole pack verified -- including ConsumerSourceTreeMismatch -- so its +// presence beside a restored binary is evidence the pack was keyed on this tree. The compiler pair +// uses one to carry the checker's input record (claim_executor --write-checker-inputs), the form of +// cargo's dep-info that survives a change of checkout, so the floor's checker-subject rule can run +// against a restored binary without a rebased copy of the old evidence. fn release_bins_pack_members(product: ReleaseBinsProduct) -> List { - match product.diagnostics { - NoBuildDiagnostics => product.roster - BuildLogMember { member: m, source: _ } => concat(product.roster, [m]) - } + concat( + match product.diagnostics { + NoBuildDiagnostics => product.roster + BuildLogMember { member: m, source: _ } => concat(product.roster, [m]) + }, + product.sidecars, + ) } // THE FLEET PACK: every enrolled release binary, built by fleet-converge's build job. fn fleet_release_bins_product() -> ReleaseBinsProduct { - ReleaseBinsProduct { kind: release_bins_key_kind, roster: ci_floor_required_artifact_names(), build_line: ci_release_build_line(), diagnostics: NoBuildDiagnostics } + ReleaseBinsProduct { kind: release_bins_key_kind, roster: ci_floor_required_artifact_names(), build_line: ci_release_build_line(), diagnostics: NoBuildDiagnostics, sidecars: [] } } // THE COMPILER PAIR: the two binaries the required floor builds anyway (gunbc.ci_release_bins @@ -191,12 +203,17 @@ fn fleet_release_bins_product() -> ReleaseBinsProduct { // consumes, so the key names the build the floor ran. It is the product the merge queue produces // as a candidate, fleet-desired publishes, and hw boot consumes -- none of which needs the other // fourteen binaries. +// claim_executor's own spelling of the sidecar beside it: _checker_inputs +// (v1_compiler cli_run checker_dependency restored_checker_inputs_path). +data compiler_pair_checker_inputs_sidecar: String = "claim_executor_checker_inputs" + fn compiler_pair_release_product() -> ReleaseBinsProduct { ReleaseBinsProduct { kind: "gunbc.compiler_pair_release_bins", roster: ci_floor_invoked_bins, build_line: repo_self_build_command(bins: ci_floor_invoked_bins), diagnostics: BuildLogMember { member: "build_diagnostics", source: floor_attempt_command_log_filename }, + sidecars: [compiler_pair_checker_inputs_sidecar], } } data release_bins_admitted_producer_ref: String = "refs/heads/main" @@ -316,6 +333,7 @@ type ReleaseBinsRefusal | ConsumerPreimageKeyMismatch | ConsumerSourceTreeMismatch | BuildDiagnosticsMissing { file: String } + | SidecarMissing { file: String } | ConsumerProductKindMismatch { kind: String } fn release_bins_refusal_name(refusal: ReleaseBinsRefusal) -> String { @@ -346,6 +364,7 @@ fn release_bins_refusal_name(refusal: ReleaseBinsRefusal) -> String { ConsumerPreimageKeyMismatch => "ConsumerPreimageKeyMismatch" ConsumerSourceTreeMismatch => "ConsumerSourceTreeMismatch" BuildDiagnosticsMissing { file: _ } => "BuildDiagnosticsMissing" + SidecarMissing { file: _ } => "SidecarMissing" ConsumerProductKindMismatch { kind: _ } => "ConsumerProductKindMismatch" } } @@ -378,6 +397,7 @@ fn release_bins_refusal_message(refusal: ReleaseBinsRefusal) -> String { ConsumerPreimageKeyMismatch => "the downloaded preimage does not hash to the build job's key" ConsumerSourceTreeMismatch => "the pack was keyed on a different source tree than this job's checkout" ConsumerProductKindMismatch { kind: k } => join(["the pack's preimage does not name the product kind ", k, " this consumer asked for; it is another product's pack"], "") + SidecarMissing { file: f } => join(["the product requires the sidecar ", f, " as a pack member and the producing job left none; a pack without it is incomplete, not smaller"], "") BuildDiagnosticsMissing { file: f } => join(["the product requires the build log ", f, " as a pack member and the build step left none; a pack without its diagnostics is incomplete, not smaller"], "") } } @@ -801,11 +821,11 @@ fn release_bins_member_checks(site: ReleaseBinsVerifySite, dir: Node, product: R } fn release_bins_promote_all(dir: Node, product: ReleaseBinsProduct) -> List { - [ + concat([ cmd(ws: [lit(t: "mkdir"), lit(t: "-p"), rooted(file: "target/release")]), in_dir(dir: rooted(file: "target/release"), command: cmd(ws: concat([lit(t: "rm"), lit(t: "-f")], release_bins_roster_words(product: product)))), in_dir(dir: dir, command: cmd(ws: concat(concat([lit(t: "cp"), lit(t: "-p")], release_bins_roster_words(product: product)), [rooted(file: "target/release/")]))), - ] + ], map(product.sidecars, f => in_dir(dir: dir, command: cmd(ws: [lit(t: "cp"), lit(t: "-p"), lit(t: f), rooted(file: "target/release/")])))) } fn release_bins_verify_pack(site: ReleaseBinsVerifySite, archive: Node, manifest: Node, dir: Node, product: ReleaseBinsProduct) -> List { @@ -1108,6 +1128,7 @@ fn release_bins_pack_statements(product: ReleaseBinsProduct) -> List { cmd(ws: [lit(t: "mkdir"), lit(t: "-p"), var(n: "STAGE")]), ], release_bins_diagnostics_capture(product: product), + release_bins_sidecar_capture(product: product), [ bash_build_with_redir_to_file( inner: bash_build_subshell_from_stmts(stmts: [bash_build_and_then( @@ -1139,6 +1160,15 @@ fn release_bins_diagnostics_capture(product: ReleaseBinsProduct) -> List { } } +// THE SIDECARS ARE ALREADY IN target/release (the producing job wrote them there), so capture only +// requires each to exist non-empty: a product that declares a sidecar and finds none refuses. +fn release_bins_sidecar_capture(product: ReleaseBinsProduct) -> List { + map(product.sidecars, f => or_refuse( + check: test_words(ws: [lit(t: "-s"), rooted(file: join(["target/release/", f], ""))]), + r: SidecarMissing { file: f }, + )) +} + // CONSUMER: a downstream job holds the key its build job published and refuses, before executing // anything it downloaded, a pack whose preimage hashes elsewhere, names another source tree, or whose // members do not match the manifest. diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index ad74101a6ce..44e637f1e21 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -1271,6 +1271,18 @@ fn compiler_gate_seed_bound_steps() -> List { role: consumes_only(capabilities: [CargoCapability]), step_name: compiler_gate_seed_build_step_name, }, + CompilerGateBoundStep { + step: compiler_gate_seed_run_step( + name: "Record the checker inputs beside the pair (build outcomes only)", + id: none, + script: "./target/release/claim_executor --write-checker-inputs target/release/claim_executor_checker_inputs\n", + env: [], + condition: Present { value: release_bins_build_condition() }, + timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, + ), + role: capability_neutral, + step_name: "Record the checker inputs beside the pair (build outcomes only)", + }, CompilerGateBoundStep { step: compiler_gate_seed_run_step( name: "Pack the compiler pair with its build log (build outcomes only)", diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index 4a032c679c7..d183720e84f 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -198,6 +198,7 @@ fn run() -> Result { let mut source_roots: Vec = Vec::new(); let mut verify_artifacts: Vec = Vec::new(); let mut verify_artifacts_mode = false; + let mut write_checker_inputs: Option = None; let mut required_floor_mode = false; let mut required_ci_mode = false; let mut required_ci_measurement_receipt: Option = None; @@ -232,6 +233,13 @@ fn run() -> Result { } break; } + // THE PRODUCER OF A RESTORED PAIR'S CHECKER-INPUT RECORD, run by the job that built the + // pair (see `write_checker_inputs_record`). Takes no roots. + "--write-checker-inputs" => { + i += 1; + write_checker_inputs = + Some(require_path_value(&args, i, "--write-checker-inputs")?); + } "--source-root" => { i += 1; source_roots.push(require_path_value(&args, i, "--source-root")?); @@ -375,6 +383,20 @@ fn run() -> Result { return verify_build_artifacts(&verify_artifacts); } + if let Some(path) = write_checker_inputs { + return match v1_compiler::cli_run::write_checker_inputs_record(std::path::Path::new(&path)) + { + Ok(n) => { + eprintln!("claim_executor: wrote {n} checker inputs to {path}"); + Ok(ExitCode::SUCCESS) + } + Err(e) => { + eprintln!("::error::write-checker-inputs refused: {e}"); + Err(ExitCode::from(1)) + } + }; + } + if let Some(path) = reach_base_standings_file { let Some(wall_ms) = claim_wall_limit_ms else { eprintln!("--reach-base-standings requires --claim-wall-limit-ms"); diff --git a/src/v1/stage0/src/cli_run.rs b/src/v1/stage0/src/cli_run.rs index 836799b9be4..2b8e13ea8d0 100644 --- a/src/v1/stage0/src/cli_run.rs +++ b/src/v1/stage0/src/cli_run.rs @@ -86,6 +86,7 @@ use serde::Serialize; mod active_workset; mod census_heads; mod checker_dependency; +pub use checker_dependency::write_checker_inputs_record; #[path = "declaration_index.rs"] pub mod declaration_index; pub mod derived_row_roster; diff --git a/src/v1/stage0/src/cli_run/checker_dependency.rs b/src/v1/stage0/src/cli_run/checker_dependency.rs index 0bb42017a9b..d51f1ce2b0e 100644 --- a/src/v1/stage0/src/cli_run/checker_dependency.rs +++ b/src/v1/stage0/src/cli_run/checker_dependency.rs @@ -121,25 +121,98 @@ pub(crate) fn checker_input_paths(prerequisites: &[String], workspace_root: &Pat inputs.into_iter().collect() } -/// Observe the checker's inputs from the dep-info beside the running executable. A missing, -/// unreadable or foreign record is a refusal the `.dag` rule consumes, never an empty set. +/// THE SIDECAR OF A RESTORED PAIR: `/_checker_inputs`, one workspace-relative +/// path per line. It is written by `write_checker_inputs_record` in the job that BUILT the pair, from +/// that build's own cargo dep-info, and travels as a verified member of the release-bins pack +/// (`gunbc.fleet_release_bins_key` `compiler_pair_release_product` `sidecars`), so a consumer that +/// restores the pair has it only after the pack's source closure was verified equal to its own tree. +/// It is a second FORM of the same fact the dep-info records, never a rebased copy of it. +pub(crate) fn restored_checker_inputs_path(exe: &Path) -> PathBuf { + let stem = exe + .file_stem() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_default(); + exe.with_file_name(format!("{stem}_checker_inputs")) +} + +/// Observe the checker's inputs: cargo's dep-info beside the running executable when this workspace +/// built it, else the verified sidecar of a restored pair. A missing, unreadable or foreign record +/// is a refusal the `.dag` rule consumes, never an empty set; the dep-info form wins when both +/// exist, because it is the build tool's own record rather than a carried one. pub(crate) fn observe_checker_input_paths(workspace_root: &Path) -> Result, String> { let exe = std::env::current_exe() .map_err(|e| format!("the running checker's own path is unreadable: {e}"))?; let mut dep_info = exe.clone().into_os_string(); dep_info.push(".d"); let dep_info = PathBuf::from(dep_info); + let text = match std::fs::read_to_string(&dep_info) { + Ok(text) => text, + Err(e) => { + let sidecar = restored_checker_inputs_path(&exe); + return match std::fs::read_to_string(&sidecar) { + Ok(carried) => checker_inputs_from_sidecar(&carried, &sidecar), + Err(_) => Err(format!( + "the checker's dependency record {} is unreadable ({e}) and no restored-pair \ + record {} exists; the floor must run a binary cargo built in this workspace \ + or restored from a verified pack", + dep_info.display(), + sidecar.display() + )), + }; + } + }; + let canonical_root = workspace_root + .canonicalize() + .unwrap_or_else(|_| workspace_root.to_path_buf()); + checker_inputs_from_record(&text, &dep_info, &canonical_root) +} + +/// The checker's inputs from a restored pair's sidecar text. The same anchor as the dep-info form +/// applies: a record that omits the file compiled into this binary describes another build. +pub(crate) fn checker_inputs_from_sidecar( + text: &str, + record: &Path, +) -> Result, String> { + let inputs: Vec = text + .lines() + .map(str::trim) + .filter(|l| !l.is_empty()) + .map(normalize_repo_path) + .collect(); + let anchor = normalize_repo_path(file!()); + if !inputs.contains(&anchor) { + return Err(format!( + "the restored-pair record {} does not list {anchor}, which this binary was compiled \ + from; it describes another build ({} inputs)", + record.display(), + inputs.len() + )); + } + Ok(inputs) +} + +/// THE PRODUCER OF THE SIDECAR: the checker's inputs from THIS build's own dep-info (the `.d` form +/// only, so a sidecar is never re-derived from a sidecar), written one path per line. Refuses when +/// the dep-info is not the cargo form. +pub fn write_checker_inputs_record(out: &Path) -> Result { + let exe = std::env::current_exe() + .map_err(|e| format!("the running checker's own path is unreadable: {e}"))?; + let mut dep_info = exe.into_os_string(); + dep_info.push(".d"); + let dep_info = PathBuf::from(dep_info); let text = std::fs::read_to_string(&dep_info).map_err(|e| { format!( - "the checker's dependency record {} is unreadable ({e}); the floor must run a binary \ - cargo built in this workspace", + "the checker's dependency record {} is unreadable ({e}); only a binary cargo built in \ + this workspace can produce the restored-pair record", dep_info.display() ) })?; - let canonical_root = workspace_root - .canonicalize() - .unwrap_or_else(|_| workspace_root.to_path_buf()); - checker_inputs_from_record(&text, &dep_info, &canonical_root) + let root = process_workspace_root(); + let root = root.canonicalize().unwrap_or(root); + let inputs = checker_inputs_from_record(&text, &dep_info, &root)?; + std::fs::write(out, inputs.join("\n") + "\n") + .map_err(|e| format!("cannot write {}: {e}", out.display()))?; + Ok(inputs.len()) } /// The checker's inputs from one record's text, or the reason the record cannot stand for them. @@ -446,6 +519,34 @@ mod tests { assert!(refusal.contains("does not list"), "{refusal}"); } + // A RESTORED PAIR'S SIDECAR is admitted only with the anchor, exactly as the dep-info form is: + // a record from another build is refused instead of read as "no checker input changed". + #[test] + fn a_restored_pair_record_is_admitted_only_with_its_anchor() { + let with_anchor = format!("src/v1/stage0/Cargo.toml\n{}\n", file!()); + let inputs = + checker_inputs_from_sidecar(&with_anchor, Path::new("claim_executor_checker_inputs")) + .expect("a record listing the anchor is the checker's inputs"); + assert!(inputs.contains(&normalize_repo_path(file!()))); + let refusal = checker_inputs_from_sidecar( + "src/v1/stage0/Cargo.toml\n", + Path::new("claim_executor_checker_inputs"), + ) + .expect_err("a record omitting the running checker's own file is refused"); + assert!(refusal.contains("does not list"), "{refusal}"); + let empty = checker_inputs_from_sidecar("", Path::new("claim_executor_checker_inputs")) + .expect_err("an empty record is refused, never read as no checker input changed"); + assert!(empty.contains("does not list"), "{empty}"); + } + + #[test] + fn the_restored_pair_record_sits_beside_the_binary_under_its_stem() { + assert_eq!( + restored_checker_inputs_path(Path::new("/w/target/release/claim_executor")), + PathBuf::from("/w/target/release/claim_executor_checker_inputs") + ); + } + fn tempdir_with_manifest() -> PathBuf { let root = std::env::temp_dir().join(format!( "checker_dependency_test_{}_{}", From b32d3b6773d20e0725d327b0c22ce7b4c60776d8 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 02:02:29 +0000 Subject: [PATCH 6/7] R1: --write-checker-inputs names an output file; do not require it to exist Co-Authored-By: Claude Sonnet 5.5 --- src/v1/stage0/src/bin/claim_executor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index d183720e84f..d335199830e 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -237,8 +237,8 @@ fn run() -> Result { // pair (see `write_checker_inputs_record`). Takes no roots. "--write-checker-inputs" => { i += 1; - write_checker_inputs = - Some(require_path_value(&args, i, "--write-checker-inputs")?); + // An OUTPUT path: it does not exist yet, so it is not resolved-and-required. + write_checker_inputs = Some(require_value(&args, i, "--write-checker-inputs")?); } "--source-root" => { i += 1; From a878bd612921417f3bad5c29b491f46af74b0b4a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 7 Oct 2026 09:49:20 +0000 Subject: [PATCH 7/7] R1: write the checker-inputs sidecar in the shared pack path so every producer of the compiler pair emits it Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/fleet-converge.yml | 1 + .github/workflows/witnesses.yml | 6 +----- dag/gunbc/fleet/fleet_release_bins_key.dag | 18 +++++++++++++++--- dag/gunbc/witness/compiler_gate_workflow.dag | 12 ------------ 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/.github/workflows/fleet-converge.yml b/.github/workflows/fleet-converge.yml index cbe7183cacb..efe10c72cc6 100644 --- a/.github/workflows/fleet-converge.yml +++ b/.github/workflows/fleet-converge.yml @@ -546,6 +546,7 @@ jobs: 'mkdir' '-p' "$STAGE" '[' '-s' "$ROOT"'/gunbc-floor-cmd.log' ']' || ('echo' '::error title=release-bins BuildDiagnosticsMissing::the product requires the build log gunbc-floor-cmd.log as a pack member and the build step left none; a pack without its diagnostics is incomplete, not smaller'; exit 1) 'cp' "$ROOT"'/gunbc-floor-cmd.log' "$ROOT"'/target/release/build_diagnostics' + './target/release/claim_executor' '--write-checker-inputs' "$ROOT"'/target/release/claim_executor_checker_inputs' '[' '-s' "$ROOT"'/target/release/claim_executor_checker_inputs' ']' || ('echo' '::error title=release-bins SidecarMissing::the product requires the sidecar claim_executor_checker_inputs as a pack member and the producing job left none; a pack without it is incomplete, not smaller'; exit 1) ('cd' "$ROOT"'/target/release' && 'sha256sum' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs') > "$STAGE"'/release-bins.manifest' 'tar' '-czf' "$STAGE"'/release-bins.tgz' '-C' "$ROOT"'/target/release' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs' diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index d750a9fd679..783658c28f5 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -320,11 +320,6 @@ jobs: 'exit' "$GUNBC_FLOOR_EXIT" if: steps.release_bins_outcome.outputs.outcome != 'reuse' - - name: Record the checker inputs beside the pair (build outcomes only) - run: | - ./target/release/claim_executor --write-checker-inputs target/release/claim_executor_checker_inputs - if: steps.release_bins_outcome.outputs.outcome != 'reuse' - timeout-minutes: 5 - name: Pack the compiler pair with its build log (build outcomes only) run: | set -e @@ -333,6 +328,7 @@ jobs: 'mkdir' '-p' "$STAGE" '[' '-s' "$ROOT"'/gunbc-floor-cmd.log' ']' || ('echo' '::error title=release-bins BuildDiagnosticsMissing::the product requires the build log gunbc-floor-cmd.log as a pack member and the build step left none; a pack without its diagnostics is incomplete, not smaller'; exit 1) 'cp' "$ROOT"'/gunbc-floor-cmd.log' "$ROOT"'/target/release/build_diagnostics' + './target/release/claim_executor' '--write-checker-inputs' "$ROOT"'/target/release/claim_executor_checker_inputs' '[' '-s' "$ROOT"'/target/release/claim_executor_checker_inputs' ']' || ('echo' '::error title=release-bins SidecarMissing::the product requires the sidecar claim_executor_checker_inputs as a pack member and the producing job left none; a pack without it is incomplete, not smaller'; exit 1) ('cd' "$ROOT"'/target/release' && 'sha256sum' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs') > "$STAGE"'/release-bins.manifest' 'tar' '-czf' "$STAGE"'/release-bins.tgz' '-C' "$ROOT"'/target/release' 'claim_executor' 'gunbc' 'build_diagnostics' 'claim_executor_checker_inputs' diff --git a/dag/gunbc/fleet/fleet_release_bins_key.dag b/dag/gunbc/fleet/fleet_release_bins_key.dag index 1add12f15f6..a85e69d4457 100644 --- a/dag/gunbc/fleet/fleet_release_bins_key.dag +++ b/dag/gunbc/fleet/fleet_release_bins_key.dag @@ -162,6 +162,7 @@ type ReleaseBinsProduct { build_line: String diagnostics: ReleaseBinsDiagnostics sidecars: List + sidecar_writer: List } // WHAT THE BUILD SAID, CARRIED AS A PACK MEMBER. A build whose warnings were dropped is not the build @@ -194,7 +195,7 @@ fn release_bins_pack_members(product: ReleaseBinsProduct) -> List { // THE FLEET PACK: every enrolled release binary, built by fleet-converge's build job. fn fleet_release_bins_product() -> ReleaseBinsProduct { - ReleaseBinsProduct { kind: release_bins_key_kind, roster: ci_floor_required_artifact_names(), build_line: ci_release_build_line(), diagnostics: NoBuildDiagnostics, sidecars: [] } + ReleaseBinsProduct { kind: release_bins_key_kind, roster: ci_floor_required_artifact_names(), build_line: ci_release_build_line(), diagnostics: NoBuildDiagnostics, sidecars: [], sidecar_writer: [] } } // THE COMPILER PAIR: the two binaries the required floor builds anyway (gunbc.ci_release_bins @@ -214,6 +215,7 @@ fn compiler_pair_release_product() -> ReleaseBinsProduct { build_line: repo_self_build_command(bins: ci_floor_invoked_bins), diagnostics: BuildLogMember { member: "build_diagnostics", source: floor_attempt_command_log_filename }, sidecars: [compiler_pair_checker_inputs_sidecar], + sidecar_writer: ["./target/release/claim_executor", "--write-checker-inputs"], } } data release_bins_admitted_producer_ref: String = "refs/heads/main" @@ -1163,10 +1165,20 @@ fn release_bins_diagnostics_capture(product: ReleaseBinsProduct) -> List { // THE SIDECARS ARE ALREADY IN target/release (the producing job wrote them there), so capture only // requires each to exist non-empty: a product that declares a sidecar and finds none refuses. fn release_bins_sidecar_capture(product: ReleaseBinsProduct) -> List { - map(product.sidecars, f => or_refuse( + concat(release_bins_sidecar_writes(product: product), map(product.sidecars, f => or_refuse( check: test_words(ws: [lit(t: "-s"), rooted(file: join(["target/release/", f], ""))]), r: SidecarMissing { file: f }, - )) + ))) +} + +// THE WRITER RUNS IN THE SHARED PACK PATH, so every producer of the product (the witnesses seed job +// and fleet-converge's CompilerPairOnly modes alike) emits the sidecar from the binary it just built; +// a product whose writer is empty declares sidecars it expects the build itself to have left. +fn release_bins_sidecar_writes(product: ReleaseBinsProduct) -> List { + match length(xs: product.sidecar_writer) == 0 { + true => [] + false => map(product.sidecars, f => cmd(ws: concat(map(product.sidecar_writer, w => lit(t: w)), [rooted(file: join(["target/release/", f], ""))]))) + } } // CONSUMER: a downstream job holds the key its build job published and refuses, before executing diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index af3e9c6870b..56cfe6e5326 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -1277,18 +1277,6 @@ fn compiler_gate_seed_bound_steps() -> List { role: consumes_only(capabilities: [CargoCapability]), step_name: compiler_gate_seed_build_step_name, }, - CompilerGateBoundStep { - step: compiler_gate_seed_run_step( - name: "Record the checker inputs beside the pair (build outcomes only)", - id: none, - script: "./target/release/claim_executor --write-checker-inputs target/release/claim_executor_checker_inputs\n", - env: [], - condition: Present { value: release_bins_build_condition() }, - timeout: Present { value: gunbc_ci_aux_step_timeout_minutes }, - ), - role: capability_neutral, - step_name: "Record the checker inputs beside the pair (build outcomes only)", - }, CompilerGateBoundStep { step: compiler_gate_seed_run_step( name: "Pack the compiler pair with its build log (build outcomes only)",