diff --git a/.gitattributes b/.gitattributes index 90733e04952..fcf268e112d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,9 +15,9 @@ .githooks/generated-artifact-merge merge=generated-artifact .githooks/pre-commit merge=generated-artifact .githooks/pre-push merge=generated-artifact +.github/workflows/branch-run-grant-broker.yml merge=generated-artifact .github/workflows/fleet-converge.yml merge=generated-artifact .github/workflows/fleet-desired.yml merge=generated-artifact -.github/workflows/heal-publish.yml merge=generated-artifact .github/workflows/heal.yml merge=generated-artifact .github/workflows/instrument-dispatch.yml merge=generated-artifact .github/workflows/microvm-shakedown.yml merge=generated-artifact diff --git a/.github/workflows/branch-run-grant-broker.yml b/.github/workflows/branch-run-grant-broker.yml new file mode 100644 index 00000000000..8c959b3e2e9 --- /dev/null +++ b/.github/workflows/branch-run-grant-broker.yml @@ -0,0 +1,77 @@ +# Generated by gunbc.branch_run_grant_broker_workflow expected_branch_run_grant_broker_workflow_yml — do not hand-edit. +# Authority: gunbc.branch_run_grant_broker_workflow branch_run_grant_broker_workflow; regen via tools.generated_artifact_gate main_wet. +name: branch-run-grant-broker +on: + workflow_run: + workflows: [fleet-converge] + branches: ["**"] + types: [requested] +env: + CARGO_TERM_COLOR: always +jobs: + broker: + runs-on: ubuntu-24.04-arm + timeout-minutes: 40 + if: github.event.workflow_run.event == 'workflow_dispatch' && github.event.workflow_run.head_repository.full_name == github.repository && (contains(github.event.workflow_run.display_title, ' hetzner_cloud_server_read ')) + permissions: + contents: read + actions: write + steps: + - name: Checkout the default branch this broker runs from + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 + with: + fetch-depth: 1 + 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: Build the broker this job runs from the default branch + 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: Dispatch the branch_run_grant run at main for the requested run attempt + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/auth/branch_run_grant_broker.dag --function broker + env: + GITHUB_TOKEN: ${{ github.token }} + BRANCH_RUN_KEY: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} + BRANCH_RUN_TITLE: ${{ github.event.workflow_run.display_title }} + timeout-minutes: 5 diff --git a/.github/workflows/fleet-converge.yml b/.github/workflows/fleet-converge.yml index 7900ff50b53..21b8cb33775 100644 --- a/.github/workflows/fleet-converge.yml +++ b/.github/workflows/fleet-converge.yml @@ -5,45 +5,24 @@ run-name: fleet-converge ${{ inputs.mode }} ${{ inputs.host }} nonce=${{ inputs. on: workflow_dispatch: inputs: + workspace_image_run_id: + description: Image build run to download for controller install; installed bytes must match the reviewed image pin + required: false + type: string host: description: Executor fleet host (runner pinned here; plan mode observes this host) required: true options: [srv1, srv2, srv3, srv4] type: choice - mode: - description: "plan shows membership hunks; apply executes a prior plan artifact; org_actions_observe validates the org credential and refuses on runner-group drift; org_runner_roster_observe reads the organization's self-hosted runner registrations to exhaustion under the same token and refuses unless the roster is complete; app_control_plane_observe reads the gunbai-ci App registration and webhook config with a short-lived App JWT and refuses on hook-config drift; app_key_version_verify reads the gunbai-ci App private key at the EXACT Secret Manager version named by app_key_version, mints an installation token with it, and refuses unless GitHub accepts it and the key's rotation deadline has not passed -- no add, disable or destroy; runner_browser_toolchain_converge installs the declared Playwright/Chromium toolchain (apt host libraries as the administrator, digest-pinned node, Playwright and Chromium archives into the job user's root) on the selected host, which must be in the pool that runs the floor job, and refuses unless every digest, version and host library reads back and headless Chromium renders a local page; microvm_host_converge installs the cited Firecracker release on the selected host and reads the kvm grant back, refusing by name when the grant has not landed (the grant itself is applied by the full-host apply spine); microvm_network_apply stages the slot and host network files the model renders at the named expected_revision as root:root 0600 in a root-only directory over the fleet SSH edge as the host's ADMINISTRATOR, installs them with the modeled operations, reloads networkd, systemd-sysctl and the nft loader unit, and reads the ruleset back -- the job user is granted none of it, because install plus systemctl over content that principal can write is arbitrary root for any pull request; guest_image_observe reports the micro-VM base artifacts and their measured digests on the selected host and builds nothing; guest_image_converge builds the runner guest image there; microvm_boot_probe boots that image and reads its serial console; spark_grants and spark_bootstrap prepare the selected Spark; spark_serving_apply applies the promoted fabric groups' pair vLLM units over the password session, workers before heads; spark_native_serving_apply relaunches group B's native four-rank arm as ONE BOUNDED TRANSACTION -- an all-host preflight that mutates nothing and refuses on any unresolved artifact from an earlier run, the incumbent UNIT preserved (plus a diagnostic inspect of the incumbent container -- the container itself cannot outlive its unit, because these run --rm in the foreground under systemd), the head applied BEFORE the workers so every rank joins the head's new rendezvous store, then a readback of the COMPLETE realization through each container's own inspect -- every field the create spec states, the effective --node-rank off that container's argv, and NCCL's typed transport verdict on that incarnation -- plus the head's front door answering with this arm's served model, and then commit or a rollback of the whole arm -- and it is a separate mode because it reloads a 328 GB arm and nobody should reach it by asking for the pair units; spark_runtime_image_probe pulls the pinned runtime image on the selected Spark and reads its capabilities from inside its own digest, changing nothing; spark_wireless_link_converge reads the selected Spark's Wi-Fi power save (runtime via iw, persisted via the NetworkManager profile), link and kernel disconnect count, sets whichever realization differs from the declared intent, and refuses unless the readback decides Noop; spark_grants_observe reads every procured Spark's sudo grant listing as gunbc-automation and the bootstrap principal, and its sudoers drop-in shape, and writes nothing; spark_v41_checkpoint_materialize fetches the admitted published DeepSeek V4.1 files onto the selected Group A Spark (about 510 GB; spark_v41_row_store_encode encodes the eight Engram row stores from the verified shards on the selected Group A Spark and reads each store's sha256; spark_v41_row_store_readback reads those stores back at their header, first and last record and every rank seam, with the published source rows at the same rows, and writes nothing; spark_v41_engram_differential compares upstream's Engram lookup kernel with the design-B file-backed lookup over sampled real rows of every row store, byte for byte, and writes nothing; it states the requirement and refuses before fetching when the disk cannot hold it), publishes each only after its sha256 matches the manifest, leaves a present file with the right digest alone and refuses one with the wrong digest, and reads the storage-backed Engram spans from the verified shards; a transfer runs detached and a rerun reattaches; spark_v41_runtime_image_build PRODUCES the DeepSeek V4.1 image on the selected Spark -- it verifies the candidate's three FlashInfer wheels against the digests the candidate keys, converges the patched source tree, builds from it, reads the produced configuration digest back from inside the image through the probe route, and admits that digest against the candidate's own recipe, refusing a digest that does not recompute from it -- and it is a separate mode from the probe because it occupies one host for hours where the probe occupies it for minutes; spark_v41_runtime_image_distribute moves that produced image, named by the configuration digest its production receipt read back, from the host that receipt names to the selected Group A Spark -- save into its fabric blob root, pulled by the target straight over the fabric rail, load -- after stating its size against every filesystem a copy lands on, leaves a target already holding the digest untouched, refuses a target holding a different image under the tag, and refuses unless the target's image inspect Id reads back as that digest; spark_v41_group_a_launch reads the production image back under its tag on every Group A host, reads its registry inside its digest and every host's occupancy, and only when Group A is suspended for this candidate with every host held and vacant stages the Engram manifest and applies the V4.1 four-rank arm as one transaction at the capacity measurement's shape -- the target names only the session host; spark_v41_serving_load runs the shared serving-load runner against the V4.1 head (target must be srv6): one vllm bench serve step per capacity-measurement concurrency, metrics scraped around each, host pressure read on every Group A rank, and the staircase stop rules applied over the worst rank; it changes no unit and writes only its receipt; host_reset_return drives a rostered reset subject through its controller and measures the return FROM A PEER, and takes reset_observer rather than host because the observer must not be the subject; runner_host_file_observe reads the runner teardown drop-in, the needrestart deferral and the loaded teardown on the selected host as its administrator and writes nothing; runner_host_file_converge writes whichever of those two files differs, reloads systemd only if the drop-in changed, and refuses unless the manager then reports the declared teardown; microvm_controller_install writes the microVM slot controller's root-owned release locus (gunbc + sources + Firecracker + jailer) and the gunbc-microvm-slot@ template unit and the gunbc-microvm-slot-reserve broker unit on srv1 and starts neither; microvm_slot_reserve starts that broker unit once on srv1, which reserves the shakedown cell through the fabric broker route from inside the root process (slot, demand and offer derived from the model, never inputs), and uploads that invocation's reservation receipt, failing unless the reservation committed; microvm_slot_start starts the shakedown slot's controller unit once on srv1 (the instance is derived from the model, never an input), waits for it bounded by the unit's own stop timeout, and uploads that invocation's controller receipt; microvm_runner_group_ensure (srv1 only) reads the organization's runner groups and, only when the microvm-shakedown group is absent, files ONE operator approval, creates it restricted to the shakedown workflow on the default branch, and refuses unless the readback holds that restriction; mtcollins1_census_qemu_host_observe reads the selected host's KVM device and, under the job user's census QEMU root, the qemu-system-aarch64 build, its ldd libraries and the AAVMF images against their pins, and writes nothing; mtcollins1_census_qemu_toolchain_converge places that root as the job user (digest-pinned noble .debs unpacked with dpkg-deb -x, no apt, no Recommends) and refuses unless the same observe reads it ready; workspace_source_pack (host=srv1) enumerates the operator workspace as the job user, drops every path the credential exclusion row names and every build output, tars exactly the remainder, reads the archive back and refuses if any member is excluded, and puts it into the workspace bucket under its SHA-256, refusing by name when the runner cannot read a source root; workspace_checkpoint_measure (host=srv3, workspace_source_object = that SHA-256) refuses unless the job user's home is on btrfs, then times a read-only subvolume snapshot, a pinned kopia scan of it, the content-addressed chunk upload and the gated revision commit, and receipts every time with the 10 s / 60 s / 10 GB draft verdicts; workspace_checkpoint_restore (host=srv3) commits a small authored workspace, puts one chunk with no head advance, destroys the directory, restores it from the head closure and requires byte-equality with the uncommitted chunk absent -- both srv3 modes refuse their commit while the R2 head's CAS ground is uncited" - required: true - options: [plan, launch_environment_plan, allocation_store_plan, workspace_commissioning_plan, apply, org_actions_observe, app_control_plane_observe, microvm_host_converge, microvm_network_observe, microvm_network_apply, guest_image_observe, guest_image_converge, microvm_boot_probe, spark_grants, spark_bootstrap, spark_serving_apply, spark_native_serving_apply, spark_runtime_image_probe, spark_wireless_link_converge, spark_grants_observe, spark_v41_checkpoint_materialize, spark_v41_row_store_encode, spark_v41_row_store_readback, spark_v41_engram_differential, spark_runtime_image_produce, spark_runtime_image_distribute, spark_arm_group_load, spark_arm_checkpoint_materialize, spark_arm_group_observe, spark_arm_group_launch_plan, spark_arm_group_launch, spark_v41_serving_load, dashboard_deploy, approval_broker_dark_install, microvm_controller_install, microvm_slot_reserve, microvm_slot_start, rlm_launch_deployment_receipt, host_reset_return, runner_host_file_observe, runner_host_file_converge, site_pxe_edge_observe, site_pxe_edge_converge, runner_password_session_tool_converge, runner_browser_toolchain_converge, r2_mint_preflight, r2_object_write_mint, org_runner_roster_observe, approval_keyring_converge, approval_device_enrolment_code_issue, mtcollins1_boot, mtcollins1_fan_observe, mtcollins1_ui_bundle_observe, mtcollins1_kvm_observer_observe, mtcollins1_census_image_publish, mtcollins1_census_member_readback, mtcollins1_census_qemu_host_observe, mtcollins1_census_qemu_toolchain_converge, host_credential_custody_converge, app_key_version_verify, r2_bucket_ensure, r2_bucket_admin_mint, r2_workspace_object_read_mint, r2_workspace_object_write_mint, r2_cache_object_read_mint, r2_cache_object_write_mint, r2_conditional_put_race_probe, workspace_source_pack, workspace_checkpoint_measure, workspace_checkpoint_restore, fabric_writer_identity_observe, pair_serving_d0, microvm_runner_group_ensure, gcp_iam_converge, namecheap_observe, printer] - type: choice - target: - description: "Spark target host for the spark_* modes, naming the administrator credential the run materializes (any rostered Spark: srv5, srv6, srv7, srv8, srv9, srv10, srv11, srv12); apply reads the subject off the plan artifact" - required: false - options: [srv5, srv6, srv7, srv8, srv9, srv10, srv11, srv12] - type: choice - runtime_image: - description: "spark_runtime_image_produce / spark_runtime_image_distribute only: which runtime image to produce on, or distribute to, the target Spark (gunbc.spark.runtime_image_produce_dispatch). A closed choice" - required: false - options: [v41-dsv41-gb10, glm53-kvplan-v2-gb10] - type: choice - checkpoint: - description: "spark_arm_checkpoint_materialize only: which checkpoint of the arm to acquire on the target Spark (gunbc.spark.arm_group_launch_dispatch). A closed choice" - required: false - options: [glm53-native-fp8, glm53-flash-nvidia-nvfp4] - type: choice - arm: - description: "spark_arm_group_launch / spark_arm_group_launch_plan only: which arm on which fabric group (gunbc.spark.arm_group_launch_dispatch). A closed choice; each arm's own inputs are its module's" - required: false - options: [v41-group-a, glm53-group-a] - type: choice - staircase_step: - description: "spark_arm_group_launch(_plan) with arm glm53-group-a only: the staircase step (gunbc.spark.glm53_arm_staircase), checkpoint and sequence ceiling over the cold 8 x 262144 floor. A closed choice; a step outside the staircase has no spelling. Must be empty for v41-group-a" + arrival_subject: + description: "arrival_converge only: the arrival-population unit (not executor host, not managed_host). Options are gunbc.machine_intake_arrival_subject arrival_converge_subject_options. Names signed off msg_f03558d1" required: false - options: [fp8-8, fp8-12, fp8-16, fp8-24, fp8-32, fp8-64, fp8-128, nvfp4-8, nvfp4-16, nvfp4-24, nvfp4-32] + options: [mtjade1] type: choice - incumbent: - description: "spark_arm_group_launch(_plan) with arm glm53-group-a only: the serve this step supersedes, as a DECLARED unit roster (never discovered): previous-step (this arm's own units from the step before) or none. Must be empty for v41-group-a" - required: false - options: [previous-step, none] + mode: + description: "Closed choice of fleet-converge modes. Contracts: gunbc.fleet_converge_workflow." + required: true + options: [plan, launch_environment_plan, allocation_store_plan, workspace_commissioning_plan, workspace_allocation_plan, workspace_image_build, apply, org_actions_observe, app_control_plane_observe, microvm_host_converge, microvm_network_observe, microvm_network_apply, guest_image_observe, guest_image_converge, microvm_boot_probe, dashboard_deploy, approval_broker_dark_install, microvm_controller_install, microvm_slot_reserve, microvm_slot_start, rlm_launch_deployment_receipt, host_reset_return, runner_host_file_observe, runner_host_file_converge, site_pxe_edge_observe, site_pxe_edge_converge, runner_password_session_tool_converge, runner_browser_toolchain_converge, r2_mint_preflight, r2_object_write_mint, org_runner_roster_observe, approval_keyring_converge, approval_device_enrolment_code_issue, mtcollins1_boot, mtcollins1_fan_observe, mtcollins1_kvm_observer_observe, mtcollins1_census_image_publish, mtcollins1_census_member_readback, mtcollins1_census_qemu_host_observe, mtcollins1_census_qemu_toolchain_converge, host_credential_custody_converge, app_key_version_verify, r2_bucket_ensure, r2_bucket_admin_mint, r2_workspace_object_read_mint, r2_workspace_object_write_mint, r2_cache_object_read_mint, r2_cache_object_write_mint, r2_conditional_put_race_probe, workspace_source_pack, workspace_checkpoint_measure, workspace_checkpoint_restore, workspace_transcript_rate_observe, fabric_writer_identity_observe, microvm_runner_group_ensure, gcp_iam_converge, namecheap_observe, printer, arrival_converge, hetzner_cloud_server_read, branch_run_grant] type: choice reset_observer: description: "host_reset_return only: the PEER that observes the reset. The subject is NOT an input -- it is derived from this observer through the modeled pairing, so a job cannot be pointed at the host it is resetting" @@ -53,12 +32,8 @@ on: credential: description: "host_credential_custody_converge only: which rostered custody row to deliver (gunbc.host_credential_custody_converge). A closed choice, never a free-text secret name: each option carries its own SecretRef, path, owner, group, mode and directory, and a row scoped to one host refuses any other" required: false - options: [controller_app_key, approval_ntfy_publisher_token, fabric_state_writer_key, oracle_oci_api_signing_key, claude_code_oauth_harness_token] + options: [controller_app_key, approval_ntfy_publisher_token, fabric_state_writer_key, cursor_worker_turn_api_key, codex_worker_turn_auth, oracle_oci_api_signing_key, claude_code_oauth_harness_token] type: choice - d0_consent: - description: "pair_serving_d0 only (Cut D, Group A on srv1; expected_revision is required and frozen with the filing): the escalation id the consent is filed under. It names the transaction, and a rerun after a crash MUST name the same id to find its frozen filing and held claim -- a fresh id is a new consent" - required: false - type: string app_key_version: description: "app_key_version_verify only: the Secret Manager version NUMBER of ci-github-app-private-key to verify. The alias latest is refused -- it names whatever is newest when it resolves, so a mint through it proves nothing about a specific key" required: false @@ -72,7 +47,7 @@ on: required: false type: string expected_revision: - description: R — the exact main revision this transaction installs and proves; every run of the transaction must execute at it (RLM_EXPECTED_REVISION). Required for dashboard_deploy and rlm_launch_deployment_receipt — and for microvm_network_apply, whose installed bytes are rendered from it + description: Optional cross-check of the event sha (RLM_EXPECTED_REVISION). Omit to admit github.sha. Present must match the checkout. Malformed refuses. rlm_launch_deployment_receipt and microvm_network_apply still require a named revision in their own admission; dashboard_deploy does not. required: false type: string apply_workflow_run_id: @@ -96,34 +71,20 @@ on: required: false options: [printer-01, printer-02] type: choice - printer_operation: - description: Observe credential identity or start one pinned project - required: false - options: [observe, print] - type: choice - printer_project_path: - description: Absolute path of staged sliced project on srv1 + printer_request: + description: "Printer operation JSON: operation (observe/print), project_path, project_sha256, credential_version, bed_clear (string true/false). The existing printer admission validates every field." required: false type: string - printer_project_sha256: - description: SHA-256 of the reviewed sliced project + agent_state_slot: + description: "workspace_source_pack and workspace_transcript_rate_observe: the Claude auth slot whose agent state is packed, as in /home/briansrls/.claude@ on srv1; admitted only if that directory exists, refused naming the observed slots otherwise; no default" required: false type: string - printer_credential_version: - description: Numeric Secret Manager version returned by observation - required: false - type: string - printer_bed_clear: - description: Operator reports this printer bed is clear for this dispatch - required: false - options: ["false", "true"] - type: choice workspace_source_object: description: "workspace_checkpoint_measure only: the lower-hex SHA-256 the workspace_source_pack receipt printed as address= (the archive's own digest; the fetched bytes must hash to it)" required: false type: string transaction_nonce: - description: "Operator-minted unique nonce for one RLM transaction; echoed into run-name so each dispatched run is API-selectable by its displayTitle, closing the run-id correlation gap (review 5062738052 B4). mtcollins1_boot only, when attempt_receipt is named: it is the boot attempt's identity, joined to the receipt's attempt and consumed once (gunbc.host_boot_attempt_admission), so a nonce admits one boot; [A-Za-z0-9_-] only" + description: "Operator-minted unique nonce for one RLM transaction; echoed into run-name so each dispatched run is API-selectable by its displayTitle, closing the run-id correlation gap (review 5062738052 B4). branch_run_grant: the requesting run's -, set by the branch-run-grant-broker workflow from GitHub's workflow_run event (gunbc.auth.branch_run_grant_broker). mtcollins1_boot only, when attempt_receipt is named: it is the boot attempt's identity, joined to the receipt's attempt and consumed once (gunbc.host_boot_attempt_admission), so a nonce admits one boot; [A-Za-z0-9_-] only" required: false type: string attempt_receipt: @@ -145,7 +106,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Isolate toolchain dirs run: |- rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo" @@ -153,7 +114,7 @@ jobs: echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" echo "MAKEFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_BUILD_JOBS=6" >> "$GITHUB_ENV" + echo "CARGO_BUILD_JOBS=5" >> "$GITHUB_ENV" if sccache --show-stats >/dev/null 2>&1; then echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV" @@ -187,7 +148,7 @@ jobs: - name: Derive release-bins key (pre-materialization axes; sha256 at cross-run scope) id: release_bins_key run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_allocation_plan' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_image_build' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') DIRTY=$('git' '-C' "$ROOT" 'status' '--porcelain' '--untracked-files=no') @@ -393,7 +354,7 @@ jobs: - name: Decide release-bins outcome; verify a stored pack completely before any restored binary runs id: release_bins_outcome run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_allocation_plan' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_image_build' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') STAGE="$ROOT"'/.release-bins-staging' @@ -531,8 +492,28 @@ jobs: - name: Build release bins (the dispatched mode's product) id: release_build run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then - cargo build --release -p v1-compiler --bin claim_executor --bin gunbc + if [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_allocation_plan' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_image_build' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then + 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" + else ROOT=$('git' 'rev-parse' '--show-toplevel') set -o pipefail @@ -566,7 +547,7 @@ jobs: timeout-minutes: 5 - name: Pack release bins with member manifest into staging (build outcomes only; tar keeps exec bits) run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_allocation_plan' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_image_build' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then set -e ROOT=$('git' 'rev-parse' '--show-toplevel') STAGE="$ROOT"'/.release-bins-staging' @@ -616,20 +597,20 @@ jobs: runs-on: ${{ fromJSON(format('["self-hosted","linux","arm64","{0}"]', github.event.inputs.host)) }} needs: [build] timeout-minutes: 295 - if: github.event.inputs.mode != 'mtcollins1_boot' && github.event.inputs.mode != 'gcp_iam_converge' && github.event.inputs.mode != 'namecheap_observe' && github.event.inputs.mode != 'printer' + if: github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'launch_environment_plan' || github.event.inputs.mode == 'allocation_store_plan' || github.event.inputs.mode == 'workspace_commissioning_plan' || github.event.inputs.mode == 'workspace_allocation_plan' || github.event.inputs.mode == 'workspace_image_build' || github.event.inputs.mode == 'apply' || github.event.inputs.mode == 'org_actions_observe' || github.event.inputs.mode == 'app_control_plane_observe' || github.event.inputs.mode == 'microvm_host_converge' || github.event.inputs.mode == 'microvm_network_observe' || github.event.inputs.mode == 'microvm_network_apply' || github.event.inputs.mode == 'guest_image_observe' || github.event.inputs.mode == 'guest_image_converge' || github.event.inputs.mode == 'microvm_boot_probe' || github.event.inputs.mode == 'spark_grants' || github.event.inputs.mode == 'spark_bootstrap' || github.event.inputs.mode == 'spark_serving_apply' || github.event.inputs.mode == 'spark_native_serving_apply' || github.event.inputs.mode == 'spark_runtime_image_probe' || github.event.inputs.mode == 'spark_wireless_link_converge' || github.event.inputs.mode == 'spark_grants_observe' || github.event.inputs.mode == 'microvm_slot_reserve' || github.event.inputs.mode == 'microvm_slot_start' || github.event.inputs.mode == 'runner_host_file_observe' || github.event.inputs.mode == 'runner_host_file_converge' || github.event.inputs.mode == 'site_pxe_edge_observe' || github.event.inputs.mode == 'site_pxe_edge_converge' || github.event.inputs.mode == 'runner_password_session_tool_converge' || github.event.inputs.mode == 'runner_browser_toolchain_converge' || github.event.inputs.mode == 'r2_mint_preflight' || github.event.inputs.mode == 'r2_object_write_mint' || github.event.inputs.mode == 'org_runner_roster_observe' || github.event.inputs.mode == 'approval_keyring_converge' || github.event.inputs.mode == 'approval_device_enrolment_code_issue' || github.event.inputs.mode == 'mtcollins1_fan_observe' || github.event.inputs.mode == 'mtcollins1_kvm_observer_observe' || github.event.inputs.mode == 'mtcollins1_census_image_publish' || github.event.inputs.mode == 'mtcollins1_census_member_readback' || github.event.inputs.mode == 'mtcollins1_census_qemu_host_observe' || github.event.inputs.mode == 'mtcollins1_census_qemu_toolchain_converge' || github.event.inputs.mode == 'host_credential_custody_converge' || github.event.inputs.mode == 'app_key_version_verify' || github.event.inputs.mode == 'r2_bucket_ensure' || github.event.inputs.mode == 'r2_bucket_admin_mint' || github.event.inputs.mode == 'r2_workspace_object_read_mint' || github.event.inputs.mode == 'r2_workspace_object_write_mint' || github.event.inputs.mode == 'r2_cache_object_read_mint' || github.event.inputs.mode == 'r2_cache_object_write_mint' || github.event.inputs.mode == 'r2_conditional_put_race_probe' || github.event.inputs.mode == 'workspace_source_pack' || github.event.inputs.mode == 'workspace_checkpoint_measure' || github.event.inputs.mode == 'workspace_checkpoint_restore' || github.event.inputs.mode == 'workspace_transcript_rate_observe' || github.event.inputs.mode == 'fabric_writer_identity_observe' || github.event.inputs.mode == 'microvm_runner_group_ensure' || github.event.inputs.mode == 'hetzner_cloud_server_read' permissions: contents: read actions: read id-token: write concurrency: - group: ${{ github.event.inputs.mode == 'spark_serving_apply' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_native_serving_apply' && 'gunbc-arm-mutation-glm-group-b-native' || github.event.inputs.mode == 'spark_v41_checkpoint_materialize' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_v41_row_store_encode' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_v41_row_store_readback' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_v41_engram_differential' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_runtime_image_produce' && format('gunbc-host-mutation-{0}', github.event.inputs.target) || github.event.inputs.mode == 'spark_runtime_image_distribute' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_arm_group_load' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_arm_checkpoint_materialize' && format('gunbc-host-mutation-{0}', github.event.inputs.target) || github.event.inputs.mode == 'spark_arm_group_observe' && format('gunbc-read-only-{0}', github.run_id) || github.event.inputs.mode == 'spark_arm_group_launch' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_v41_serving_load' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'approval_device_enrolment_code_issue' && 'gunbc-subject-mutation-approval-device-store-srv1' || github.event.inputs.mode == 'r2_conditional_put_race_probe' && 'r2-conditional-put-race-probe' || github.event.inputs.mode == 'pair_serving_d0' && 'gunbc-host-mutation-srv1' || format('gunbc-host-mutation-{0}', github.event.inputs.host) }} + group: ${{ github.event.inputs.mode == 'spark_serving_apply' && 'gunbc-host-mutation-srv1' || github.event.inputs.mode == 'spark_native_serving_apply' && 'gunbc-arm-mutation-glm-group-b-native' || github.event.inputs.mode == 'approval_device_enrolment_code_issue' && 'gunbc-subject-mutation-approval-device-store-srv1' || github.event.inputs.mode == 'r2_conditional_put_race_probe' && 'r2-conditional-put-race-probe' || format('gunbc-host-mutation-{0}', github.event.inputs.host) }} cancel-in-progress: false steps: - name: Checkout uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Download release-bins artifact uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -639,7 +620,7 @@ jobs: - name: Verify the handed-off release bins against the build key, then promote and run --verify-build-artifacts (fail-closed) id: release_bins run: | - if [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ]; then + if [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_allocation_plan' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_image_build' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'approval_device_enrolment_code_issue' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_source_pack' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_measure' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_checkpoint_restore' ] || [ "${FLEET_CONVERGE_MODE:-}" = 'workspace_transcript_rate_observe' ]; then 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) @@ -757,17 +738,6 @@ jobs: RELEASE_BINS_KEY: ${{ needs.build.outputs.release_bins_key }} FLEET_CONVERGE_MODE: ${{ github.event.inputs.mode }} timeout-minutes: 5 - - name: "Pair-serving D0 admission (credential-free): the executor is srv1, the consent and the expected revision are named" - id: pair_serving_d0_admit - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/pair_serving_d0_door.dag --function pair_serving_d0_admit_executor - env: - FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} - GUNBC_D0_CONSENT: ${{ github.event.inputs.d0_consent }} - RLM_EXPECTED_REVISION: ${{ github.event.inputs.expected_revision }} - if: github.event.inputs.mode == 'pair_serving_d0' - timeout-minutes: 15 - name: WIF auth (OIDC -> fleet-cloud-convergence SA, access token only) id: wif_auth uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 @@ -776,6 +746,26 @@ jobs: service_account: fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com token_format: access_token create_credentials_file: false + if: "!(github.event.inputs.mode == 'hetzner_cloud_server_read') && !(github.event.inputs.mode == 'workspace_image_build' || github.event.inputs.mode == 'workspace_allocation_plan')" + timeout-minutes: 5 + - name: "Run-scoped grant: wait for the grant run main's broker dispatched for this run attempt" + id: branch_run_grant_await + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/auth/branch_run_grant_await.dag --function await_grant + env: + GITHUB_TOKEN: ${{ github.token }} + if: github.event.inputs.mode == 'hetzner_cloud_server_read' + timeout-minutes: 50 + - name: WIF auth (OIDC -> branch-dispatch pool -> hetzner-cloud-control SA; only after this run's grant) + id: wif_auth_hetzner_cloud_control + uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 + with: + workload_identity_provider: projects/582015116396/locations/global/workloadIdentityPools/github-fleet-converge-dispatch/providers/github-fleet-converge-dispatch-oidc + service_account: hetzner-cloud-control@gunbai-secrets.iam.gserviceaccount.com + token_format: access_token + create_credentials_file: false + if: github.event.inputs.mode == 'hetzner_cloud_server_read' timeout-minutes: 5 - name: Materialize fleet key in-run (SM versions/1 pinned -> RUNNER_TEMP 0600 -> ssh-agent -> wipe file) run: |- @@ -804,7 +794,7 @@ jobs: echo "fleet-key: agent loaded (identity fleet-automation@gunbc; secret versions/1 pinned; fingerprint verified against modeled authority; key file wiped)" env: WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'launch_environment_plan' || github.event.inputs.mode == 'allocation_store_plan' || github.event.inputs.mode == 'workspace_commissioning_plan' || github.event.inputs.mode == 'apply' || github.event.inputs.mode == 'app_control_plane_observe' || github.event.inputs.mode == 'microvm_host_converge' || github.event.inputs.mode == 'microvm_network_observe' || github.event.inputs.mode == 'microvm_network_apply' || github.event.inputs.mode == 'guest_image_observe' || github.event.inputs.mode == 'guest_image_converge' || github.event.inputs.mode == 'microvm_boot_probe' || github.event.inputs.mode == 'spark_grants' || github.event.inputs.mode == 'spark_bootstrap' || github.event.inputs.mode == 'spark_serving_apply' || github.event.inputs.mode == 'spark_native_serving_apply' || github.event.inputs.mode == 'spark_runtime_image_probe' || github.event.inputs.mode == 'spark_wireless_link_converge' || github.event.inputs.mode == 'spark_grants_observe' || github.event.inputs.mode == 'spark_v41_checkpoint_materialize' || github.event.inputs.mode == 'spark_v41_row_store_encode' || github.event.inputs.mode == 'spark_v41_row_store_readback' || github.event.inputs.mode == 'spark_v41_engram_differential' || github.event.inputs.mode == 'spark_runtime_image_produce' || github.event.inputs.mode == 'spark_runtime_image_distribute' || github.event.inputs.mode == 'spark_arm_group_load' || github.event.inputs.mode == 'spark_arm_checkpoint_materialize' || github.event.inputs.mode == 'spark_arm_group_observe' || github.event.inputs.mode == 'spark_arm_group_launch_plan' || github.event.inputs.mode == 'spark_arm_group_launch' || github.event.inputs.mode == 'spark_v41_serving_load' || github.event.inputs.mode == 'dashboard_deploy' || github.event.inputs.mode == 'approval_broker_dark_install' || github.event.inputs.mode == 'microvm_controller_install' || github.event.inputs.mode == 'rlm_launch_deployment_receipt' || github.event.inputs.mode == 'host_reset_return' || github.event.inputs.mode == 'runner_host_file_observe' || github.event.inputs.mode == 'runner_host_file_converge' || github.event.inputs.mode == 'site_pxe_edge_observe' || github.event.inputs.mode == 'site_pxe_edge_converge' || github.event.inputs.mode == 'runner_password_session_tool_converge' || github.event.inputs.mode == 'runner_browser_toolchain_converge' || github.event.inputs.mode == 'r2_mint_preflight' || github.event.inputs.mode == 'r2_object_write_mint' || github.event.inputs.mode == 'approval_keyring_converge' || github.event.inputs.mode == 'approval_device_enrolment_code_issue' || github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'mtcollins1_census_image_publish' || github.event.inputs.mode == 'mtcollins1_census_member_readback' || github.event.inputs.mode == 'host_credential_custody_converge' || github.event.inputs.mode == 'pair_serving_d0' + if: github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'launch_environment_plan' || github.event.inputs.mode == 'allocation_store_plan' || github.event.inputs.mode == 'workspace_commissioning_plan' || github.event.inputs.mode == 'apply' || github.event.inputs.mode == 'app_control_plane_observe' || github.event.inputs.mode == 'microvm_host_converge' || github.event.inputs.mode == 'microvm_network_observe' || github.event.inputs.mode == 'microvm_network_apply' || github.event.inputs.mode == 'guest_image_observe' || github.event.inputs.mode == 'guest_image_converge' || github.event.inputs.mode == 'microvm_boot_probe' || github.event.inputs.mode == 'spark_grants' || github.event.inputs.mode == 'spark_bootstrap' || github.event.inputs.mode == 'spark_serving_apply' || github.event.inputs.mode == 'spark_native_serving_apply' || github.event.inputs.mode == 'spark_runtime_image_probe' || github.event.inputs.mode == 'spark_wireless_link_converge' || github.event.inputs.mode == 'spark_grants_observe' || github.event.inputs.mode == 'dashboard_deploy' || github.event.inputs.mode == 'approval_broker_dark_install' || github.event.inputs.mode == 'microvm_controller_install' || github.event.inputs.mode == 'rlm_launch_deployment_receipt' || github.event.inputs.mode == 'host_reset_return' || github.event.inputs.mode == 'runner_host_file_observe' || github.event.inputs.mode == 'runner_host_file_converge' || github.event.inputs.mode == 'site_pxe_edge_observe' || github.event.inputs.mode == 'site_pxe_edge_converge' || github.event.inputs.mode == 'runner_password_session_tool_converge' || github.event.inputs.mode == 'runner_browser_toolchain_converge' || github.event.inputs.mode == 'r2_mint_preflight' || github.event.inputs.mode == 'r2_object_write_mint' || github.event.inputs.mode == 'approval_keyring_converge' || github.event.inputs.mode == 'approval_device_enrolment_code_issue' || github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'mtcollins1_census_image_publish' || github.event.inputs.mode == 'mtcollins1_census_member_readback' || github.event.inputs.mode == 'host_credential_custody_converge' timeout-minutes: 5 - name: Fleet converge plan (membership_reconcile → artifact) id: plan @@ -843,6 +833,40 @@ jobs: RLM_EXPECTED_REVISION: ${{ github.event.inputs.expected_revision }} if: github.event.inputs.mode == 'workspace_commissioning_plan' timeout-minutes: 15 + - name: "Workspace: workspace_allocation_plan" + id: workspace_allocation_plan + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/fleet/fleet_converge_plan_cli.dag --function fleet_converge_workspace_allocation_plan_wet + env: + FLEET_CONVERGE_EXPECTED_HOST: ${{ inputs.host }} + RLM_EXPECTED_REVISION: ${{ inputs.expected_revision }} + if: github.event.inputs.mode == 'workspace_allocation_plan' + timeout-minutes: 30 + - name: "Workspace: workspace_image_build" + id: workspace_image_build + run: | + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + fakeroot -- "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/workspace_image_delivery.dag --function workspace_image_build_wet + env: + FLEET_CONVERGE_EXPECTED_HOST: ${{ inputs.host }} + RLM_EXPECTED_REVISION: ${{ inputs.expected_revision }} + if: github.event.inputs.mode == 'workspace_image_build' + timeout-minutes: 30 + - name: Archive the exact workspace image build and digest receipt + id: workspace_image_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: workspace-guest-image + path: |- + target/workspace-image-artifact/workspace.ext4 + target/workspace-image-artifact/base/vmlinux-6.1.128 + target/workspace-image-artifact/build-receipt.txt + if-no-files-found: error + retention-days: 90 + compression-level: 6 + if: github.event.inputs.mode == 'workspace_image_build' + timeout-minutes: 10 - name: Upload fleet converge plan artifact id: plan_upload uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f @@ -852,7 +876,7 @@ jobs: if-no-files-found: error retention-days: 30 compression-level: 0 - if: github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'launch_environment_plan' || github.event.inputs.mode == 'allocation_store_plan' || github.event.inputs.mode == 'workspace_commissioning_plan' + if: github.event.inputs.mode == 'plan' || github.event.inputs.mode == 'launch_environment_plan' || github.event.inputs.mode == 'allocation_store_plan' || github.event.inputs.mode == 'workspace_commissioning_plan' || github.event.inputs.mode == 'workspace_allocation_plan' timeout-minutes: 10 - name: Download fleet converge plan artifact id: plan_download @@ -1142,38 +1166,6 @@ jobs: retention-days: 30 if: github.event.inputs.mode == 'mtcollins1_fan_observe' timeout-minutes: 10 - - name: "Mt. Collins served UI: firmware revision, then source.min.js, viewer.html and every script they load, each with its sha256 (reads only)" - id: mtcollins1_ui_bundle_observe - run: | - 'set' '-euo' 'pipefail' - 'umask' '077' - HDR_FILE="$RUNNER_TEMP"'/mtcollins1-ui-bundle-auth-header' - RESP_FILE="$RUNNER_TEMP"'/mtcollins1-ui-bundle-sm.json' - DEST_FILE="$RUNNER_TEMP"'/mtcollins1-ui-bundle-bmc-credential' - 'trap' ''\''rm'\'' '\''-f'\'' "$HDR_FILE" "$RESP_FILE" "$DEST_FILE" - ' 'EXIT' - 'printf' 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - 'curl' '-sSf' '--connect-timeout' '10' '--max-time' '60' '-o' "$RESP_FILE" '-H' '@'"$HDR_FILE" 'https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc/versions/2:access' && 'python3' '-c' 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$DEST_FILE" - 'chmod' '600' "$DEST_FILE" - 'export' 'GUNBC_MTCOLLINS1_BMC_CREDENTIAL_FILE='"$DEST_FILE" - ROOT=$('git' 'rev-parse' '--show-toplevel' 2>/dev/null || 'pwd') - - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/machine_intake/mtcollins1_ui_bundle_observe.dag --function mtcollins1_ui_bundle_observe_wet - env: - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'mtcollins1_ui_bundle_observe' - timeout-minutes: 25 - - name: Upload Mt. Collins served UI files and receipt - id: mtcollins1_ui_bundle_observe_upload - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: mtcollins1-ui-bundle - path: target/mtcollins1-ui-bundle-* - if-no-files-found: error - retention-days: 90 - compression-level: 0 - if: always() && github.event.inputs.mode == 'mtcollins1_ui_bundle_observe' - timeout-minutes: 10 - name: "Mt. Collins KVM observer, no boot: under the unit hold, log in, establish the viewer, one still, release" id: mtcollins1_kvm_observer_observe run: | @@ -1237,27 +1229,6 @@ jobs: compression-level: 0 if: always() && github.event.inputs.mode == 'fabric_writer_identity_observe' timeout-minutes: 10 - - name: "Pair-serving D0 (Cut D): file the consent, wait for the operator, suspend the group's authority for the keyed successor under the admitted grant" - id: pair_serving_d0 - run: |- - # 🟡 dissolve-on: gunbc_ci_pair_serving_d0_invoke - orch-emitted foreign-executor (GitHub Actions run:) credential runner: WIF access token by env, ONE pinned submission-MAC secret version fetched over curl through the shared auth header and SM decode, a 0600 file under RUNNER_TEMP with a trap armed BEFORE the key touches disk. The pipeline steps are modeled (gunbc_ci_pair_serving_d0_prelude) but the runner transport itself remains hand-shell; DISSOLVES WHEN the runner is authored as v2.extdeps.languages.bash_build nodes (typed, grammar-quoted words; command substitution; assignment; pipe; if/test; redirect; || true) emitted by v2.workflow.bash_emit bash_emit_stmts -- a route AVAILABLE today (#12422 retired the sibling pack runner on it), so this migration is unauthored, not blocked on a missing capability; set -euo pipefail, umask, export and trap are ordinary commands on that route, and the trap body is itself built from nodes and passed as one grammar-quoted word, so no new orchestration intent is a prerequisite - set -euo pipefail - umask 077 - HDR_FILE="$RUNNER_TEMP/pair-serving-d0-auth-header" - RESP_FILE="$RUNNER_TEMP/pair-serving-d0-sm.json" - SUB_FILE="$RUNNER_TEMP/approval-submission-mac-key" - trap 'rm -f "$HDR_FILE" "$RESP_FILE" "$SUB_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - if DEST_FILE="$SUB_FILE"; curl -sSf --connect-timeout 10 --max-time 60 -o "$RESP_FILE" -H @"$HDR_FILE" "https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key/versions/1:access" && python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$DEST_FILE"; then chmod 600 "$SUB_FILE"; export GUNBC_APPROVAL_SUBMISSION_MAC_KEY_FILE="$SUB_FILE"; else rm -f "$SUB_FILE"; echo 'submission MAC key not materialized: a run that must file will refuse there; a held lifecycle resumes without it'; fi - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/pair_serving_d0_door.dag --function pair_serving_d0_ci_wet - env: - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} - GUNBC_D0_CONSENT: ${{ github.event.inputs.d0_consent }} - RLM_EXPECTED_REVISION: ${{ github.event.inputs.expected_revision }} - if: github.event.inputs.mode == 'pair_serving_d0' - timeout-minutes: 47 - name: "Mt. Collins census image: over fleet SSH to srv2, reproduce the pinned image in scratch, then build and publish the current derivation create-only in /srv/bmc" id: mtcollins1_census_image_publish run: |- @@ -1448,6 +1419,25 @@ jobs: retention-days: 30 if: always() && github.event.inputs.mode == 'r2_workspace_object_write_mint' timeout-minutes: 10 + - name: Hetzner Cloud acquired-server read (GET /v1/servers/{id}; changes nothing) + id: hetzner_cloud_server_read + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/fleet/hetzner_cloud_server_control.dag --function read + env: + WIF_ACCESS_TOKEN: ${{ steps.wif_auth_hetzner_cloud_control.outputs.access_token }} + if: github.event.inputs.mode == 'hetzner_cloud_server_read' + timeout-minutes: 5 + - name: Upload Hetzner Cloud server read receipt (ids, names, statuses; no secret) + id: hetzner_cloud_server_read_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: hetzner-cloud-server-read-receipt + path: target/hetzner-cloud-server-read-receipt.txt + if-no-files-found: warn + retention-days: 30 + if: always() && github.event.inputs.mode == 'hetzner_cloud_server_read' + timeout-minutes: 10 - name: R2 cache-blobs object-read token mint (AccountTokens.Create + Secret Manager custody) id: r2_cache_object_read_mint run: |- @@ -1514,6 +1504,7 @@ jobs: cat "$ROOT/target/workspace-source-pack-receipt.txt" env: WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + WORKSPACE_AGENT_STATE_SLOT: ${{ github.event.inputs.agent_state_slot }} if: github.event.inputs.mode == 'workspace_source_pack' timeout-minutes: 120 - name: Upload workspace source pack receipt @@ -1570,6 +1561,27 @@ jobs: compression-level: 0 if: always() && github.event.inputs.mode == 'workspace_checkpoint_restore' timeout-minutes: 10 + - name: "Workspace transcript append rate: size and mtime of every transcript file on srv1, sampled per interval over a declared window, content never read" + id: workspace_transcript_rate_observe + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/fabric/workspace_transcript_rate.dag --function workspace_transcript_rate_observe_wet + cat "$ROOT/target/workspace-transcript-rate-receipt.txt" + env: + WORKSPACE_AGENT_STATE_SLOT: ${{ github.event.inputs.agent_state_slot }} + if: github.event.inputs.mode == 'workspace_transcript_rate_observe' + timeout-minutes: 30 + - name: Upload workspace transcript append-rate receipt + id: workspace_transcript_rate_observe_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: workspace-transcript-rate-observe + path: target/workspace-transcript-rate-receipt.txt + if-no-files-found: error + retention-days: 90 + compression-level: 0 + if: always() && github.event.inputs.mode == 'workspace_transcript_rate_observe' + timeout-minutes: 10 - name: Materialize approval MAC keys on srv1 (SM pinned -> sha256 prefix -> /etc/gunbc-roadmap 0640) id: approval_keyring_converge run: |- @@ -1829,7 +1841,7 @@ jobs: HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac + case "${SPARK_CONVERGE_TARGET:-}" in *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi @@ -1858,7 +1870,7 @@ jobs: HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac + case "${SPARK_CONVERGE_TARGET:-}" in *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi @@ -1887,7 +1899,7 @@ jobs: HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac + case "${SPARK_CONVERGE_TARGET:-}" in *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi @@ -1916,7 +1928,7 @@ jobs: HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac + case "${SPARK_CONVERGE_TARGET:-}" in *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi @@ -1945,7 +1957,7 @@ jobs: HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac + case "${SPARK_CONVERGE_TARGET:-}" in *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi @@ -1974,7 +1986,7 @@ jobs: HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac + case "${SPARK_CONVERGE_TARGET:-}" in *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi @@ -2001,468 +2013,72 @@ jobs: cat "$ROOT/target/spark-exact-grant-probe-receipt.txt" if: github.event.inputs.mode == 'spark_grants_observe' timeout-minutes: 60 - - name: Spark V4.1 checkpoint materialize (fetch the admitted published files onto the selected Group A Spark, verify each sha256 before publishing, read the storage-backed Engram spans) - id: spark_v41_checkpoint_materialize + - name: "Runner host files: observe the teardown drop-in, the needrestart deferral and the loaded teardown (no writes)" + id: runner_host_file_observe run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/v41_checkpoint_materialize.dag --function v41_checkpoint_materialize_ci_wet - cat "$ROOT/target/v41-checkpoint-materialize-receipt.txt" + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_host_file_converge.dag --function runner_host_file_observe_ci_wet + cat "$ROOT/target/runner-host-file-converge-receipt.txt" env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'spark_v41_checkpoint_materialize' - timeout-minutes: 60 - - name: Spark V4.1 index selection (verify the pinned index on the selected Group A Spark and read its .engram. selection with parse_json_document, in its own process) - id: spark_v41_index_selection + FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} + if: github.event.inputs.mode == 'runner_host_file_observe' + timeout-minutes: 15 + - name: "Runner host files: write what differs as the administrator, reload if the drop-in changed, read the loaded teardown back" + id: runner_host_file_converge run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/v41_checkpoint_materialize.dag --function v41_index_selection_ci_wet - cat "$ROOT/target/v41-index-selection-receipt.txt" + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_host_file_converge.dag --function runner_host_file_converge_ci_wet + cat "$ROOT/target/runner-host-file-converge-receipt.txt" env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: always() && github.event.inputs.mode == 'spark_v41_checkpoint_materialize' - timeout-minutes: 60 - - name: Spark V4.1 row store encode (encode the eight Engram row stores from the verified shards on the selected Group A Spark and read each store's sha256) - id: spark_v41_row_store_encode + FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} + if: github.event.inputs.mode == 'runner_host_file_converge' + timeout-minutes: 15 + - name: "Password-session tools: ensure every tool password_session_host_cli_requirements enrolls is present on this runner, installing only what is measured absent" + id: runner_password_session_tool_converge run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/v41_row_store_encode_run.dag --function v41_row_store_encode_ci_wet - cat "$ROOT/target/v41-row-store-encode-receipt.txt" + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_password_session_tool_converge.dag --function runner_password_session_tool_converge_ci_wet + cat "$ROOT/target/runner-password-session-tool-converge-receipt.txt" env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'spark_v41_row_store_encode' - timeout-minutes: 60 - - name: Spark V4.1 row store readback (read the eight Engram row stores' header, first and last record and the published source rows at every rank seam on the selected Group A Spark, writing nothing) - id: spark_v41_row_store_readback + FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} + if: github.event.inputs.mode == 'runner_password_session_tool_converge' + timeout-minutes: 15 + - name: Upload password-session tool converge receipt + id: runner_password_session_tool_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: runner-password-session-tool-converge-receipt + path: target/runner-password-session-tool-converge-receipt.txt + if-no-files-found: error + retention-days: 30 + if: always() && (github.event.inputs.mode == 'runner_password_session_tool_converge') + timeout-minutes: 10 + - name: "Browser toolchain: apt host libraries as the administrator, digest-pinned node/Playwright/Chromium archives into the job user's root, then read back digests, versions, libraries and a headless render" + id: runner_browser_toolchain_converge run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/v41_row_store_readback_run.dag --function v41_row_store_readback_ci_wet - cat "$ROOT/target/v41-row-store-readback-receipt.txt" + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_browser_toolchain.dag --function runner_browser_toolchain_converge_ci_wet + cat "$ROOT/target/runner-browser-toolchain-converge-receipt.txt" env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'spark_v41_row_store_readback' - timeout-minutes: 60 - - name: Spark V4.1 Engram differential (upstream lookup kernel vs the design-B file-backed lookup over sampled real rows of every row store, byte for byte, on the selected Group A Spark, read-only) - id: spark_v41_engram_differential + FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} + if: github.event.inputs.mode == 'runner_browser_toolchain_converge' + timeout-minutes: 15 + - name: Upload browser toolchain converge receipt + id: runner_browser_toolchain_receipt_upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: runner-browser-toolchain-converge-receipt + path: target/runner-browser-toolchain-converge-receipt.txt + if-no-files-found: error + retention-days: 30 + if: always() && (github.event.inputs.mode == 'runner_browser_toolchain_converge') + timeout-minutes: 10 + - name: "Host credential custody: deliver the chosen rostered credential from Secret Manager at its row's owner, group and mode, read owner, mode, directory, bytes and staging back" + id: host_credential_custody_converge run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/v41_engram_differential_run.dag --function v41_engram_differential_ci_wet - cat "$ROOT/target/v41-engram-differential-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'spark_v41_engram_differential' - timeout-minutes: 60 - - name: "Spark runtime image produce (the image named by the closed runtime_image input, gunbc.spark.runtime_image_produce_dispatch: built on the selected Group A Spark, read back by digest and probed inside that digest)" - id: spark_runtime_image_produce - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/runtime_image_produce_dispatch.dag --function runtime_image_produce_ci_wet - cat "$ROOT/target/runtime-image-produce-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_RUNTIME_IMAGE: ${{ github.event.inputs.runtime_image }} - if: github.event.inputs.mode == 'spark_runtime_image_produce' - timeout-minutes: 180 - - name: Spark runtime image distribute (the produced image named by the closed runtime_image input, by its configuration digest, from the host its production receipt names to the selected Spark of its group, read back there) - id: spark_runtime_image_distribute - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/runtime_image_produce_dispatch.dag --function runtime_image_distribute_ci_wet - cat "$ROOT/target/runtime-image-distribute-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_RUNTIME_IMAGE: ${{ github.event.inputs.runtime_image }} - if: github.event.inputs.mode == 'spark_runtime_image_distribute' - timeout-minutes: 120 - - name: Spark arm checkpoint materialize (the checkpoint named by the closed checkpoint input, adopted or fetched into the target Spark's Hub cache and verified) - id: spark_arm_checkpoint_materialize - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/arm_group_launch_dispatch.dag --function arm_checkpoint_materialize_ci - cat "$ROOT/target/arm-checkpoint-materialize-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_ARM_LAUNCH: ${{ github.event.inputs.arm }} - GUNBC_ARM_STAIRCASE_STEP: ${{ github.event.inputs.staircase_step }} - GUNBC_ARM_INCUMBENT: ${{ github.event.inputs.incumbent }} - GUNBC_ARM_CHECKPOINT: ${{ github.event.inputs.checkpoint }} - GUNBC_RUNTIME_IMAGE: ${{ github.event.inputs.runtime_image }} - if: github.event.inputs.mode == 'spark_arm_checkpoint_materialize' - timeout-minutes: 180 - - name: "Spark arm group OBSERVE (read-only: every host's running units, containers and GPU processes with their owning cgroups; nothing is changed)" - id: spark_arm_group_observe - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/arm_group_launch_dispatch.dag --function arm_group_observe_ci - cat "$ROOT/target/arm-group-observe-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_ARM_LAUNCH: ${{ github.event.inputs.arm }} - GUNBC_ARM_STAIRCASE_STEP: ${{ github.event.inputs.staircase_step }} - GUNBC_ARM_INCUMBENT: ${{ github.event.inputs.incumbent }} - if: github.event.inputs.mode == 'spark_arm_group_observe' - timeout-minutes: 60 - - name: Spark arm group launch PLAN (the chosen arm's plan, units, scripts, declared incumbent and admission verdict; no host is touched) - id: spark_arm_group_launch_plan - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/arm_group_launch_dispatch.dag --function arm_group_launch_plan_ci - cat "$ROOT/target/arm-group-launch-plan.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_ARM_LAUNCH: ${{ github.event.inputs.arm }} - GUNBC_ARM_STAIRCASE_STEP: ${{ github.event.inputs.staircase_step }} - GUNBC_ARM_INCUMBENT: ${{ github.event.inputs.incumbent }} - if: github.event.inputs.mode == 'spark_arm_group_launch_plan' - timeout-minutes: 60 - - name: Spark arm group launch (the chosen arm's reads and admission, its pre-apply, the declared incumbent stopped and the hosts re-read vacant, then the arm applied as one transaction; the incumbent restored on anything but a commit) - id: spark_arm_group_launch - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/arm_group_launch_dispatch.dag --function arm_group_launch_ci_wet - cat "$ROOT/target/arm-group-launch-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_ARM_LAUNCH: ${{ github.event.inputs.arm }} - GUNBC_ARM_STAIRCASE_STEP: ${{ github.event.inputs.staircase_step }} - GUNBC_ARM_INCUMBENT: ${{ github.event.inputs.incumbent }} - if: github.event.inputs.mode == 'spark_arm_group_launch' - timeout-minutes: 110 - - name: Spark arm group load (the staircase step named by the closed inputs, loaded at its cold floor and its ceiling from its head; stop rules judged; calibration receipt folded) - id: spark_arm_group_load - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/arm_group_launch_dispatch.dag --function arm_group_load_ci - cat "$ROOT/target/arm-group-load-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - GUNBC_ARM_LAUNCH: ${{ github.event.inputs.arm }} - GUNBC_ARM_STAIRCASE_STEP: ${{ github.event.inputs.staircase_step }} - GUNBC_ARM_INCUMBENT: ${{ github.event.inputs.incumbent }} - if: github.event.inputs.mode == 'spark_arm_group_load' - timeout-minutes: 60 - - name: "Spark V4.1 serving-load staircase (shared serving-load runner: per concurrency scrape, vllm bench serve, scrape, with rank pressure on srv5-8; the staircase's stop rules over the worst rank)" - id: spark_v41_serving_load - run: |- - set -euo pipefail - umask 077 - CRED_FILE="$RUNNER_TEMP/spark-administrator-credential" - RESP_FILE="$RUNNER_TEMP/spark-administrator-response.json" - HDR_FILE="$RUNNER_TEMP/spark-administrator-auth-header" - trap 'rm -f "$CRED_FILE" "$RESP_FILE" "$HDR_FILE"' EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - case "${SPARK_CONVERGE_TARGET:-}" in srv5) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv6) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv7) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv8) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv9) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv10) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv11) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; srv12) SM_URL="https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/spark-administrator-password/versions/1:access"; SM_RES="projects/gunbai-secrets/secrets/spark-administrator-password/versions/1" ;; *) echo "SparkAdministratorCredentialHostUnrostered: SPARK_CONVERGE_TARGET='${SPARK_CONVERGE_TARGET:-}' names no host in the administrator credential roster; contacting NO host" >&2; exit 1 ;; esac - HTTP_CODE="$(curl -sS -o "$RESP_FILE" -w '%{http_code}' -H @"$HDR_FILE" "$SM_URL" || echo 000)" - if [ "$HTTP_CODE" = 403 ]; then echo "SparkAdministratorCredentialUnreadable: $SM_RES answered 403 to serviceAccount:fleet-cloud-convergence@gunbai-secrets.iam.gserviceaccount.com. Secret Manager returns 403 both when a version exists and is not readable AND when it does not exist, so this run cannot tell those apart -- REMEDY EITHER WAY: confirm the version exists and grant that principal roles/secretmanager.secretAccessor on it. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" = 404 ]; then echo "SparkAdministratorCredentialAbsent: $SM_RES answered 404; the modeled reference names a version Secret Manager does not hold. Contacting NO host." >&2; exit 1; fi - if [ "$HTTP_CODE" != 200 ]; then echo "SparkAdministratorCredentialFetchFailed: HTTP $HTTP_CODE from $SM_RES; contacting NO host." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$RESP_FILE" > "$CRED_FILE" - rm -f "$RESP_FILE" - chmod 600 "$CRED_FILE" - if [ ! -s "$CRED_FILE" ]; then echo "SparkAdministratorCredentialEmpty: $SM_RES returned 200 with an empty payload, which is an enrolled-but-blank secret and never a blank password; contacting NO host" >&2; exit 1; fi - export GUNBC_SPARK_BOOTSTRAP_CREDENTIAL_FILE="$CRED_FILE" - echo "spark-admin-credential: materialized from $SM_RES to a 0600 run-local file, consumed and removed within this one step" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/spark/v41_serving_load.dag --function v41_serving_load_ci_wet - cat "$ROOT/target/v41-serving-load-receipt.txt" - env: - SPARK_CONVERGE_TARGET: ${{ github.event.inputs.target }} - WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} - if: github.event.inputs.mode == 'spark_v41_serving_load' - timeout-minutes: 60 - - name: "Runner host files: observe the teardown drop-in, the needrestart deferral and the loaded teardown (no writes)" - id: runner_host_file_observe - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_host_file_converge.dag --function runner_host_file_observe_ci_wet - cat "$ROOT/target/runner-host-file-converge-receipt.txt" - env: - FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} - if: github.event.inputs.mode == 'runner_host_file_observe' - timeout-minutes: 15 - - name: "Runner host files: write what differs as the administrator, reload if the drop-in changed, read the loaded teardown back" - id: runner_host_file_converge - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_host_file_converge.dag --function runner_host_file_converge_ci_wet - cat "$ROOT/target/runner-host-file-converge-receipt.txt" - env: - FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} - if: github.event.inputs.mode == 'runner_host_file_converge' - timeout-minutes: 15 - - name: "Password-session tools: ensure every tool password_session_host_cli_requirements enrolls is present on this runner, installing only what is measured absent" - id: runner_password_session_tool_converge - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_password_session_tool_converge.dag --function runner_password_session_tool_converge_ci_wet - cat "$ROOT/target/runner-password-session-tool-converge-receipt.txt" - env: - FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} - if: github.event.inputs.mode == 'runner_password_session_tool_converge' - timeout-minutes: 15 - - name: Upload password-session tool converge receipt - id: runner_password_session_tool_receipt_upload - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: runner-password-session-tool-converge-receipt - path: target/runner-password-session-tool-converge-receipt.txt - if-no-files-found: error - retention-days: 30 - if: always() && (github.event.inputs.mode == 'runner_password_session_tool_converge') - timeout-minutes: 10 - - name: "Browser toolchain: apt host libraries as the administrator, digest-pinned node/Playwright/Chromium archives into the job user's root, then read back digests, versions, libraries and a headless render" - id: runner_browser_toolchain_converge - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/runner/runner_browser_toolchain.dag --function runner_browser_toolchain_converge_ci_wet - cat "$ROOT/target/runner-browser-toolchain-converge-receipt.txt" - env: - FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} - if: github.event.inputs.mode == 'runner_browser_toolchain_converge' - timeout-minutes: 15 - - name: Upload browser toolchain converge receipt - id: runner_browser_toolchain_receipt_upload - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: runner-browser-toolchain-converge-receipt - path: target/runner-browser-toolchain-converge-receipt.txt - if-no-files-found: error - retention-days: 30 - if: always() && (github.event.inputs.mode == 'runner_browser_toolchain_converge') - timeout-minutes: 10 - - name: "Host credential custody: deliver the chosen rostered credential from Secret Manager at its row's owner, group and mode, read owner, mode, directory, bytes and staging back" - id: host_credential_custody_converge - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/fleet/host_credential_custody_converge.dag --function host_credential_custody_converge_ci_wet - cat "$ROOT/target/host-credential-custody-converge-receipt.txt" + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/fleet/host_credential_custody_converge.dag --function host_credential_custody_converge_ci_wet + cat "$ROOT/target/host-credential-custody-converge-receipt.txt" env: FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} FLEET_CONVERGE_CUSTODY_CREDENTIAL: ${{ github.event.inputs.credential }} @@ -2549,7 +2165,7 @@ jobs: echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" echo "MAKEFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_BUILD_JOBS=6" >> "$GITHUB_ENV" + echo "CARGO_BUILD_JOBS=5" >> "$GITHUB_ENV" if sccache --show-stats >/dev/null 2>&1; then echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV" @@ -2752,7 +2368,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Download release-bins artifact uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -2855,6 +2471,7 @@ jobs: dashboard-deploy: runs-on: ${{ fromJSON(format('["self-hosted","linux","arm64","{0}"]', github.event.inputs.host)) }} needs: [build] + environment: srv1-production timeout-minutes: 50 if: github.event.inputs.mode == 'dashboard_deploy' concurrency: @@ -2865,7 +2482,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Download release-bins artifact uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -2989,6 +2606,7 @@ jobs: approval-broker-dark-install: runs-on: ${{ fromJSON(format('["self-hosted","linux","arm64","{0}"]', github.event.inputs.host)) }} needs: [build] + environment: srv1-production timeout-minutes: 50 if: github.event.inputs.mode == 'approval_broker_dark_install' concurrency: @@ -2999,7 +2617,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Download release-bins artifact uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -3088,6 +2706,7 @@ jobs: microvm-controller-install: runs-on: ${{ fromJSON(format('["self-hosted","linux","arm64","{0}"]', github.event.inputs.host)) }} needs: [build] + environment: srv1-production timeout-minutes: 50 if: github.event.inputs.mode == 'microvm_controller_install' concurrency: @@ -3098,7 +2717,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Download release-bins artifact uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -3178,6 +2797,15 @@ jobs: env: RELEASE_BINS_KEY: ${{ needs.build.outputs.release_bins_key }} timeout-minutes: 5 + - name: Download the workspace image selected for installation + id: workspace_image_download + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + with: + name: workspace-guest-image + path: target/workspace-image-artifact + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ inputs.workspace_image_run_id }} + timeout-minutes: 10 - name: Install the microVM slot controller release locus, VMM + jailer and template unit on srv1 (additive; starts no instance) id: microvm_controller_install run: |- @@ -3201,7 +2829,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with: fetch-depth: 0 - ref: ${{ (github.event.inputs.mode == 'mtcollins1_boot' || github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'namecheap_observe' || github.event.inputs.mode == 'printer') && github.sha || github.event.inputs.expected_revision || github.sha }} + ref: ${{ github.sha }} - name: Download release-bins artifact uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: @@ -3378,7 +3006,7 @@ jobs: needs: [build] environment: gcp-iam-converge timeout-minutes: 60 - if: github.event.inputs.mode == 'gcp_iam_converge' + if: github.event.inputs.mode == 'gcp_iam_converge' || github.event.inputs.mode == 'branch_run_grant' permissions: contents: read actions: read @@ -3498,6 +3126,17 @@ jobs: WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} if: github.event.inputs.mode == 'gcp_iam_converge' timeout-minutes: 40 + - name: "Branch run grant: read the run from GitHub and the account's policy as iam-observe, file one request, wait for the operator" + id: branch_run_grant_request + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/auth/branch_run_grant.dag --function branch_run_grant_request_wet + env: + WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + GITHUB_TOKEN: ${{ github.token }} + BRANCH_RUN_KEY: ${{ github.event.inputs.transaction_nonce }} + if: github.event.inputs.mode == 'branch_run_grant' + timeout-minutes: 40 - name: WIF auth (OIDC -> dedicated gcp-iam-converge pool -> iam-converge SA; only after a live approval) id: wif_auth_iam_converge uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 @@ -3507,7 +3146,7 @@ jobs: token_format: access_token access_token_lifetime: 600s create_credentials_file: false - if: github.event.inputs.mode == 'gcp_iam_converge' && steps.gcp_iam_request.outputs.approved == 'true' + if: (github.event.inputs.mode == 'gcp_iam_converge' && steps.gcp_iam_request.outputs.approved == 'true') || (github.event.inputs.mode == 'branch_run_grant' && steps.branch_run_grant_request.outputs.approved == 'true') timeout-minutes: 5 - name: "GCP IAM converge: re-plan, re-admit the approval, apply exactly the approved effects as iam-converge, read back as iam-observe" id: gcp_iam_apply @@ -3519,6 +3158,18 @@ jobs: IAM_CONVERGE_ACCESS_TOKEN: ${{ steps.wif_auth_iam_converge.outputs.access_token }} if: github.event.inputs.mode == 'gcp_iam_converge' && steps.gcp_iam_request.outputs.approved == 'true' timeout-minutes: 15 + - name: "Branch run grant: re-derive, re-admit the approval, add exactly the approved run cell as iam-converge, read back as iam-observe" + id: branch_run_grant_apply + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/auth/branch_run_grant.dag --function branch_run_grant_apply_wet + env: + WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + GITHUB_TOKEN: ${{ github.token }} + BRANCH_RUN_KEY: ${{ github.event.inputs.transaction_nonce }} + IAM_CONVERGE_ACCESS_TOKEN: ${{ steps.wif_auth_iam_converge.outputs.access_token }} + if: github.event.inputs.mode == 'branch_run_grant' && steps.branch_run_grant_request.outputs.approved == 'true' + timeout-minutes: 15 namecheap-observe: runs-on: ${{ fromJSON(format('["self-hosted","linux","arm64","{0}"]', github.event.inputs.host)) }} needs: [build] @@ -3643,6 +3294,132 @@ jobs: compression-level: 0 if: success() timeout-minutes: 10 + arrival-converge: + runs-on: [self-hosted, linux, arm64, srv1] + needs: [build] + environment: mtjade1-arrival + timeout-minutes: 25 + if: github.event.inputs.mode == 'arrival_converge' + permissions: + contents: read + actions: read + id-token: write + concurrency: + group: gunbc-host-mutation-srv1 + cancel-in-progress: false + steps: + - name: Checkout (the event sha only; no dispatch input selects these bytes) + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 + with: + fetch-depth: 0 + ref: ${{ github.sha }} + - 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 handed-off release bins against the build key, then promote and run --verify-build-artifacts (fail-closed) + id: release_bins + 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 + discover_source_root_ingest + claim_batch + interp_recorded_fixture_witness + v1_src_dag_parse + auth_declared_but_unwired_witness + bootstrap_witness + dag_collect_fingerprint_witness + diagnostics_witness + effects_rest_transport_witness + infer_semantics_witness + parse_witness + cssl_assemble + namespace_structural_root_exposure_generated_witness + codex_app_server_stdio_session + ' > "$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 + discover_source_root_ingest:regular file:1 + claim_batch:regular file:1 + interp_recorded_fixture_witness:regular file:1 + v1_src_dag_parse:regular file:1 + auth_declared_but_unwired_witness:regular file:1 + bootstrap_witness:regular file:1 + dag_collect_fingerprint_witness:regular file:1 + diagnostics_witness:regular file:1 + effects_rest_transport_witness:regular file:1 + infer_semantics_witness:regular file:1 + parse_witness:regular file:1 + cssl_assemble:regular file:1 + namespace_structural_root_exposure_generated_witness:regular file:1 + codex_app_server_stdio_session:regular file:1 + ' > "$RUNNER_TEMP"'/member-types.expected' + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'stat' '-c' '%n:%F:%h' 'claim_executor' 'gunbc' 'discover_source_root_ingest' 'claim_batch' 'interp_recorded_fixture_witness' 'v1_src_dag_parse' 'auth_declared_but_unwired_witness' 'bootstrap_witness' 'dag_collect_fingerprint_witness' 'diagnostics_witness' 'effects_rest_transport_witness' 'infer_semantics_witness' 'parse_witness' 'cssl_assemble' 'namespace_structural_root_exposure_generated_witness' 'codex_app_server_stdio_session') > "$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' 'discover_source_root_ingest' 'claim_batch' 'interp_recorded_fixture_witness' 'v1_src_dag_parse' 'auth_declared_but_unwired_witness' 'bootstrap_witness' 'dag_collect_fingerprint_witness' 'diagnostics_witness' 'effects_rest_transport_witness' 'infer_semantics_witness' 'parse_witness' 'cssl_assemble' 'namespace_structural_root_exposure_generated_witness' 'codex_app_server_stdio_session' '-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' 'discover_source_root_ingest' 'claim_batch' 'interp_recorded_fixture_witness' 'v1_src_dag_parse' 'auth_declared_but_unwired_witness' 'bootstrap_witness' 'dag_collect_fingerprint_witness' 'diagnostics_witness' 'effects_rest_transport_witness' 'infer_semantics_witness' 'parse_witness' 'cssl_assemble' 'namespace_structural_root_exposure_generated_witness' 'codex_app_server_stdio_session') + ('cd' "$RUNNER_TEMP"'/release-bins-check' && 'cp' '-p' 'claim_executor' 'gunbc' 'discover_source_root_ingest' 'claim_batch' 'interp_recorded_fixture_witness' 'v1_src_dag_parse' 'auth_declared_but_unwired_witness' 'bootstrap_witness' 'dag_collect_fingerprint_witness' 'diagnostics_witness' 'effects_rest_transport_witness' 'infer_semantics_witness' 'parse_witness' 'cssl_assemble' 'namespace_structural_root_exposure_generated_witness' 'codex_app_server_stdio_session' "$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' "$ROOT"'/target/release/discover_source_root_ingest' "$ROOT"'/target/release/claim_batch' "$ROOT"'/target/release/interp_recorded_fixture_witness' "$ROOT"'/target/release/v1_src_dag_parse' "$ROOT"'/target/release/auth_declared_but_unwired_witness' "$ROOT"'/target/release/bootstrap_witness' "$ROOT"'/target/release/dag_collect_fingerprint_witness' "$ROOT"'/target/release/diagnostics_witness' "$ROOT"'/target/release/effects_rest_transport_witness' "$ROOT"'/target/release/infer_semantics_witness' "$ROOT"'/target/release/parse_witness' "$ROOT"'/target/release/cssl_assemble' "$ROOT"'/target/release/namespace_structural_root_exposure_generated_witness' "$ROOT"'/target/release/codex_app_server_stdio_session' + env: + RELEASE_BINS_KEY: ${{ needs.build.outputs.release_bins_key }} + timeout-minutes: 5 + - name: "Arrival admission (credential-free): store host srv1 and a population arrival_subject" + id: arrival_admit + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/machine_intake/arrival_admission.dag --function arrival_admit_executor + env: + FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} + FLEET_CONVERGE_ARRIVAL_SUBJECT: ${{ github.event.inputs.arrival_subject }} + timeout-minutes: 5 + - name: WIF auth (OIDC -> dedicated mtjade1-arrival pool -> mtjade1-arrival SA, access token only) + id: wif_auth + uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 + with: + workload_identity_provider: projects/582015116396/locations/global/workloadIdentityPools/github-mtjade1-arrival/providers/github-mtjade1-arrival-oidc + service_account: mtjade1-arrival@gunbai-secrets.iam.gserviceaccount.com + token_format: access_token + create_credentials_file: false + timeout-minutes: 5 + - name: arrival_converge under the standing grant and arrival interlock + id: arrival_converge + run: |- + ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) + "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/machine_intake/arrival_run.dag --function arrival_converge_wet + env: + WIF_ACCESS_TOKEN: ${{ steps.wif_auth.outputs.access_token }} + FLEET_CONVERGE_EXPECTED_HOST: ${{ github.event.inputs.host }} + FLEET_CONVERGE_ARRIVAL_SUBJECT: ${{ github.event.inputs.arrival_subject }} + timeout-minutes: 5 printer: runs-on: [self-hosted, linux, arm64, srv1] needs: [build] @@ -3762,11 +3539,11 @@ jobs: GUNBC_APPROVAL_CAPABILITY_MAC_KEY_FILE: /etc/gunbc-roadmap/approval-mac-key GUNBC_APPROVAL_STORE_RECEIPT_MAC_KEY_FILE: /etc/gunbc-roadmap/approval-store-receipt-mac-key PRINTER_TARGET: ${{ inputs.printer }} - PRINTER_OPERATION: ${{ inputs.printer_operation }} - PRINTER_PROJECT_PATH: ${{ inputs.printer_project_path }} - PRINTER_PROJECT_SHA256: ${{ inputs.printer_project_sha256 }} - PRINTER_CREDENTIAL_VERSION: ${{ inputs.printer_credential_version }} - PRINTER_BED_CLEAR: ${{ inputs.printer_bed_clear }} + PRINTER_OPERATION: ${{ fromJSON(inputs.printer_request || '{}').operation }} + PRINTER_PROJECT_PATH: ${{ fromJSON(inputs.printer_request || '{}').project_path }} + PRINTER_PROJECT_SHA256: ${{ fromJSON(inputs.printer_request || '{}').project_sha256 }} + PRINTER_CREDENTIAL_VERSION: ${{ fromJSON(inputs.printer_request || '{}').credential_version }} + PRINTER_BED_CLEAR: ${{ fromJSON(inputs.printer_request || '{}').bed_clear }} timeout-minutes: 15 - name: Upload printer operation receipt id: printer_receipt diff --git a/.github/workflows/heal-publish.yml b/.github/workflows/heal-publish.yml deleted file mode 100644 index 45b5d38d383..00000000000 --- a/.github/workflows/heal-publish.yml +++ /dev/null @@ -1,173 +0,0 @@ -# Generated by gunbc.heal_publisher_workflow expected_heal_publisher_workflow_yml — do not hand-edit. -# Authority: gunbc.heal_publisher_workflow heal_publisher_workflow; regen via tools.generated_artifact_gate main_wet. -name: heal-publish -on: - workflow_run: - workflows: [witnesses] - branches: ["**"] - types: [completed] -concurrency: - queue: max - group: heal-publish-${{ github.event.workflow_run.head_branch }} - cancel-in-progress: false -env: - CARGO_TERM_COLOR: always -jobs: - publish: - runs-on: ubuntu-24.04-arm - timeout-minutes: 45 - if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name == github.repository - permissions: - contents: read - pull-requests: read - actions: read - id-token: write - steps: - - name: Ask the triggering run whether it sealed a candidate - id: candidate_probe - run: |- - set -euo pipefail - NAMES=$(gh api --paginate "repos/$GITHUB_REPOSITORY/actions/runs/$TRIGGERING_RUN_ID/artifacts" --jq '.artifacts[].name') - if printf '%s\n' "$NAMES" | grep -qx "heal-repair-candidate"; then - echo "present=true" >> "$GITHUB_OUTPUT" - else - echo "present=false" >> "$GITHUB_OUTPUT" - echo "::notice::the triggering run sealed no repair candidate, so there is nothing to publish" - fi - env: - GH_TOKEN: ${{ github.token }} - TRIGGERING_RUN_ID: ${{ github.event.workflow_run.id }} - - name: Checkout the default branch this publisher runs from - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 - with: - fetch-depth: 1 - persist-credentials: false - - name: Download the candidate the triggering heal run produced - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 - with: - pattern: heal-repair-candidate - path: heal-candidate - merge-multiple: true - run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ github.token }} - if: steps.candidate_probe.outputs.present == 'true' - - 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" - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' - - name: Install Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 - with: - components: rustfmt - cache: false - rustflags: -D warnings - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' - - 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" - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' - - name: Build the publisher this job runs from the default branch - 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" - - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' - - name: Admit the candidate against this checkout's policy, with no publication credential - run: |- - set -euo pipefail - GUNBC_CG=/sys/fs/cgroup/gunbc-publish - echo "+memory" | sudo tee /sys/fs/cgroup/cgroup.subtree_control >/dev/null - sudo mkdir -p "$GUNBC_CG" - echo 12884901888 | sudo tee "$GUNBC_CG/memory.max" >/dev/null - echo $$ | sudo tee "$GUNBC_CG/cgroup.procs" >/dev/null - cat "$GUNBC_CG/memory.max" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/heal_publication.dag --function heal_publication_admit_wet - env: - HEAL_CANDIDATE_DIRECTORY: heal-candidate - GITHUB_TOKEN: ${{ github.token }} - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' - - name: Federate to the publisher's own identity (access token only, five minutes) - id: heal_publisher_wif - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 - with: - workload_identity_provider: projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher/providers/github-heal-publisher-oidc - service_account: heal-publisher@gunbai-secrets.iam.gserviceaccount.com - token_format: access_token - access_token_lifetime: 300s - create_credentials_file: false - export_environment_variables: false - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' - - name: Publish the admitted repair, fast-forward only, and read it back - run: |- - set -euo pipefail - GUNBC_CG=/sys/fs/cgroup/gunbc-publish - echo "+memory" | sudo tee /sys/fs/cgroup/cgroup.subtree_control >/dev/null - sudo mkdir -p "$GUNBC_CG" - echo 12884901888 | sudo tee "$GUNBC_CG/memory.max" >/dev/null - echo $$ | sudo tee "$GUNBC_CG/cgroup.procs" >/dev/null - cat "$GUNBC_CG/memory.max" - set -euo pipefail - umask 077 - KEY_FILE="$RUNNER_TEMP/heal-publisher-app-key" - HDR_FILE="$RUNNER_TEMP/heal-publisher-auth-header" - JWT_FILE="$RUNNER_TEMP/heal-publisher-app-jwt" - TOKEN_HEADERS="$RUNNER_TEMP/heal-publisher-token-headers" - TOKEN_BODY="$RUNNER_TEMP/heal-publisher-token-body" - cleanup() { rm -f "$KEY_FILE" "$HDR_FILE" "$JWT_FILE" "$TOKEN_HEADERS" "$TOKEN_BODY" "$RUNNER_TEMP/heal-publisher-app-key-sm-body"; unset GITHUB_TOKEN; } - trap cleanup EXIT - printf 'Authorization: Bearer %s\n' "$WIF_ACCESS_TOKEN" > "$HDR_FILE" - SM_BODY="$RUNNER_TEMP/heal-publisher-app-key-sm-body" - SM_CODE="$(curl -sS -o "$SM_BODY" -w '%{http_code}' -H @"$HDR_FILE" "https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key/versions/latest:access")" - rm -f "$HDR_FILE" - if [ "$SM_CODE" != 200 ]; then rm -f "$SM_BODY"; echo "HealPublisherAppKeyUnreadable: Secret Manager answered HTTP $SM_CODE for projects/gunbai-secrets/secrets/agent-github-app-private-key/versions/latest to the heal-publisher@gunbai-secrets.iam.gserviceaccount.com principal. 403 means the version exists and is not readable OR does not exist -- REMEDY: run gunbc.auth.heal_publisher_provision heal_publisher_federation_provision_with_supplied_token, which creates that identity and binds roles/secretmanager.secretAccessor on the key for that service account only after reading the federation back. Minting NO token, so nothing is published." >&2; exit 1; fi - python3 -c 'import sys,json,base64; sys.stdout.buffer.write(base64.b64decode(json.load(open(sys.argv[1]))["payload"]["data"]))' "$SM_BODY" > "$KEY_FILE" - rm -f "$SM_BODY" - chmod 600 "$KEY_FILE" - NOW=$(date +%s) - b64url() { base64 -w0 | tr '+/' '-_' | tr -d '='; } - JWT_HEADER=$(printf '%s' '{"alg":"RS256","typ":"JWT"}' | b64url) - JWT_PAYLOAD=$(printf '{"iat":%d,"exp":%d,"iss":"%s"}' "$((NOW-60))" "$((NOW+540))" "3909950" | b64url) - JWT_SIG=$(printf '%s.%s' "$JWT_HEADER" "$JWT_PAYLOAD" | openssl dgst -sha256 -sign "$KEY_FILE" | b64url) - rm -f "$KEY_FILE" - printf 'Authorization: Bearer %s.%s.%s\n' "$JWT_HEADER" "$JWT_PAYLOAD" "$JWT_SIG" > "$JWT_FILE" - curl -sS -X POST -H @"$JWT_FILE" -H 'Accept: application/vnd.github+json' -H 'Content-Type: application/json' -d '{"repositories": ["gunbc"], "permissions": {"contents": "write"}}' -D "$TOKEN_HEADERS" -o "$TOKEN_BODY" "https://api.github.com/app/installations/136673607/access_tokens" - rm -f "$JWT_FILE" - if ! head -n1 "$TOKEN_HEADERS" | grep -q ' 201 '; then echo "HealPublisherInstallationTokenRefused: GitHub answered $(head -n1 "$TOKEN_HEADERS" | tr -d '\r') for the publisher App's installation; a revoked key, a removed installation, or a Contents permission that is no longer write each look like this -- inspect the App's installation on this repository" >&2; exit 1; fi - GITHUB_TOKEN="$(python3 -c 'import sys,json; print(json.load(open(sys.argv[1]))["token"])' "$TOKEN_BODY")" - rm -f "$TOKEN_HEADERS" "$TOKEN_BODY" - export GITHUB_TOKEN - echo "heal-publisher: installation token minted in-run for app gunbai-bot installation 136673607 (one hour, contents:write on this repository only; key wiped; token held in this step's environment only)" - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/heal_publication.dag --function heal_publication_publish_wet - env: - HEAL_CANDIDATE_DIRECTORY: heal-candidate - WIF_ACCESS_TOKEN: ${{ steps.heal_publisher_wif.outputs.access_token }} - if: hashFiles('heal-candidate/heal-repair-candidate-blob-*') != '' diff --git a/.github/workflows/microvm-shakedown.yml b/.github/workflows/microvm-shakedown.yml index 15cf8639986..fe717050aa5 100644 --- a/.github/workflows/microvm-shakedown.yml +++ b/.github/workflows/microvm-shakedown.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: inputs: attempt: - description: The attempt's registration name (its transient unit name, gunbc.runner_jit_mint jit_runner_name_for). It is REQUIRED in runs-on, so a wrong name queues forever rather than running on any other runner. + description: The attempt's registration name (its transient unit name, gunbc.runner.jit_registration_subject jit_runner_name_for). It is REQUIRED in runs-on, so a wrong name queues forever rather than running on any other runner. required: true type: string jobs: diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 4455164783a..19a7d67a1da 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -19,73 +19,14 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: CARGO_TERM_COLOR: always - GUNBC_REQUIRED_CI_CONTRACT_EPOCH: 2026-09-01.1 + GUNBC_REQUIRED_CI_CONTRACT_EPOCH: 2026-10-09.1 jobs: - rust-unit-tests: - runs-on: [self-hosted, linux, arm64] - timeout-minutes: 25 - if: github.event_name != 'pull_request' - permissions: - contents: read - steps: - - name: Checkout - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 - with: - 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: Run the v1 unit tests - id: rust_unit_tests - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'cargo test --release -p v1-compiler --lib') 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" - - timeout-minutes: 15 - seed: + witnesses: 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 @@ -113,176 +54,6 @@ 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: Key the compiler pair (stamps the build with the materialized tree) - id: release_bins_key - run: | - set -e - ROOT=$('git' 'rev-parse' '--show-toplevel') - DIRTY=$('git' '-C' "$ROOT" 'status' '--porcelain' '--untracked-files=no') - '[' '-z' "$DIRTY" ']' || ('echo' '::error title=release-bins SourceTreeDirty::tracked files differ from HEAD, so HEAD^{tree} does not name the build input; refusing to key'; exit 1) - 'env' '-0' > "$RUNNER_TEMP"'/env.nul' - if 'grep' '-zqE' '^(CARGO|RUST|CC|CXX|AR|CFLAGS|CXXFLAGS|LDFLAGS|TARGET_CC|HOST_CC)[A-Za-z0-9_]*(TOKEN|CREDENTIAL|SECRET|PASSWORD|PASSWD|AUTH)[A-Za-z0-9_]*=' "$RUNNER_TEMP"'/env.nul'; then ('echo' '::error title=release-bins CredentialInBuildEnvironment::a registry or toolchain credential is present in the build environment; credentials are not build inputs and must not reach a key or a published preimage, so the build is refused'; exit 1); fi - if 'grep' '-zqE' '^(RUSTC|RUSTC_WORKSPACE_WRAPPER|CARGO_BUILD_RUSTC|CARGO_BUILD_RUSTC_WRAPPER|CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER|CARGO_BUILD_TARGET|CARGO_BUILD_TARGET_DIR|CARGO_TARGET_DIR|CARGO_TARGET_[A-Z0-9_]*_(LINKER|RUNNER))=' "$RUNNER_TEMP"'/env.nul'; then ('echo' '::error title=release-bins BuildOverrideNotAdmitted::the environment overrides the compiler, linker, target or target directory Cargo would use; the key probes only the admitted defaults, so the build is refused rather than keyed wrongly'; exit 1); fi - 'sed' '-z' '-nE' '/^(CARGO_[A-Z0-9_]*|RUST[A-Z0-9_]*|CC|CXX|AR|CFLAGS|CXXFLAGS|LDFLAGS|CC_[A-Za-z0-9_]*|CFLAGS_[A-Za-z0-9_]*|TARGET_CC|HOST_CC)=/p' "$RUNNER_TEMP"'/env.nul' > "$RUNNER_TEMP"'/env-keyed.nul' - 'sed' '-z' '-E' '/^(CARGO_HOME|RUSTUP_HOME|CARGO_BIN|CARGO_TERM_COLOR|CARGO_BUILD_JOBS|RUSTC_WRAPPER)=/d' "$RUNNER_TEMP"'/env-keyed.nul' > "$RUNNER_TEMP"'/env-kept.nul' - 'sort' '-z' "$RUNNER_TEMP"'/env-kept.nul' > "$RUNNER_TEMP"'/env-sorted.nul' - 'sha256sum' "$RUNNER_TEMP"'/env-sorted.nul' > "$RUNNER_TEMP"'/AXIS_ENVIRONMENT_DIGEST.out' - AXIS_ENVIRONMENT_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/AXIS_ENVIRONMENT_DIGEST.out') - ANCESTOR_0="$ROOT" - ANCESTOR_CONFIG='' - ANCESTOR_1=$('dirname' "$ANCESTOR_0") - ('[' '!' '-e' "$ANCESTOR_1"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_1"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_1" - ANCESTOR_2=$('dirname' "$ANCESTOR_1") - ('[' '!' '-e' "$ANCESTOR_2"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_2"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_2" - ANCESTOR_3=$('dirname' "$ANCESTOR_2") - ('[' '!' '-e' "$ANCESTOR_3"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_3"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_3" - ANCESTOR_4=$('dirname' "$ANCESTOR_3") - ('[' '!' '-e' "$ANCESTOR_4"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_4"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_4" - ANCESTOR_5=$('dirname' "$ANCESTOR_4") - ('[' '!' '-e' "$ANCESTOR_5"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_5"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_5" - ANCESTOR_6=$('dirname' "$ANCESTOR_5") - ('[' '!' '-e' "$ANCESTOR_6"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_6"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_6" - ANCESTOR_7=$('dirname' "$ANCESTOR_6") - ('[' '!' '-e' "$ANCESTOR_7"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_7"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_7" - ANCESTOR_8=$('dirname' "$ANCESTOR_7") - ('[' '!' '-e' "$ANCESTOR_8"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_8"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_8" - ANCESTOR_9=$('dirname' "$ANCESTOR_8") - ('[' '!' '-e' "$ANCESTOR_9"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_9"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_9" - ANCESTOR_10=$('dirname' "$ANCESTOR_9") - ('[' '!' '-e' "$ANCESTOR_10"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_10"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_10" - ANCESTOR_11=$('dirname' "$ANCESTOR_10") - ('[' '!' '-e' "$ANCESTOR_11"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_11"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_11" - ANCESTOR_12=$('dirname' "$ANCESTOR_11") - ('[' '!' '-e' "$ANCESTOR_12"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_12"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_12" - ANCESTOR_13=$('dirname' "$ANCESTOR_12") - ('[' '!' '-e' "$ANCESTOR_13"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_13"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_13" - ANCESTOR_14=$('dirname' "$ANCESTOR_13") - ('[' '!' '-e' "$ANCESTOR_14"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_14"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_14" - ANCESTOR_15=$('dirname' "$ANCESTOR_14") - ('[' '!' '-e' "$ANCESTOR_15"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_15"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_15" - ANCESTOR_16=$('dirname' "$ANCESTOR_15") - ('[' '!' '-e' "$ANCESTOR_16"'/.cargo/config.toml' ']' && '[' '!' '-e' "$ANCESTOR_16"'/.cargo/config' ']') || ANCESTOR_CONFIG="$ANCESTOR_16" - if '[' '-n' "$ANCESTOR_CONFIG" ']'; then 'echo' 'release-bins: Cargo config found above the checkout in '"$ANCESTOR_CONFIG"; ('echo' '::error title=release-bins AncestorCargoConfigPresent::a Cargo config file exists in a directory above the checkout; it is outside the committed tree the key covers, so the build is refused'; exit 1); fi - '[' "$ANCESTOR_16" '=' '/' ']' || ('echo' '::error title=release-bins CheckoutPathTooDeep::the checkout is deeper than the ancestor Cargo-config scan covers, so ancestor configuration cannot be ruled out'; 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" - EXPECTED="$RUNNER_TEMP"'/release-bins.preimage.expected' - AXIS_PRODUCER=$("$CARGO_BIN" '-V') - '[' '-n' "$AXIS_PRODUCER" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input AXIS_PRODUCER read back empty, so the key would not name the build; refusing to key'; exit 1) - 'rustc' '-vV' > "$RUNNER_TEMP"'/rustc-host.out' - AXIS_HOST=$('sed' '-n' 's/^host: //p' "$RUNNER_TEMP"'/rustc-host.out') - 'cc' '--version' > "$RUNNER_TEMP"'/AXIS_CC.out' - AXIS_CC=$('head' '-n' '1' "$RUNNER_TEMP"'/AXIS_CC.out') - 'ld' '--version' > "$RUNNER_TEMP"'/AXIS_LD.out' - AXIS_LD=$('head' '-n' '1' "$RUNNER_TEMP"'/AXIS_LD.out') - 'ldd' '--version' > "$RUNNER_TEMP"'/AXIS_LIBC.out' - AXIS_LIBC=$('head' '-n' '1' "$RUNNER_TEMP"'/AXIS_LIBC.out') - '[' '-n' "$AXIS_HOST" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input AXIS_HOST read back empty, so the key would not name the build; refusing to key'; exit 1) - '[' '-n' "$AXIS_CC" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input AXIS_CC read back empty, so the key would not name the build; refusing to key'; exit 1) - '[' '-n' "$AXIS_LD" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input AXIS_LD read back empty, so the key would not name the build; refusing to key'; exit 1) - '[' '-n' "$AXIS_LIBC" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input AXIS_LIBC read back empty, so the key would not name the build; refusing to key'; exit 1) - 'rustc' '-vV' > "$RUNNER_TEMP"'/rustc-vV.out' - AXIS_TOOLCHAIN=$('paste' '-s' '-d' ';' "$RUNNER_TEMP"'/rustc-vV.out') - '[' '-n' "$AXIS_TOOLCHAIN" ']' || ('echo' '::error title=release-bins AxisValueUnread::the key input AXIS_TOOLCHAIN read back empty, so the key would not name the build; refusing to key'; exit 1) - ('printf' 'kind=%s\n' 'gunbc.compiler_pair_release_bins'; 'printf' 'address=sha256 scope=cross_run\n'; 'printf' 'producer_compiler=%s\n' "$AXIS_PRODUCER"; 'printf' 'source_closure=%s\n' "$SOURCE_CLOSURE"; 'printf' 'target_model=%s;cc=%s;ld=%s;libc=%s\n' "$AXIS_HOST" "$AXIS_CC" "$AXIS_LD" "$AXIS_LIBC"; 'printf' 'toolchain=%s\n' "$AXIS_TOOLCHAIN"; 'printf' 'build_configuration=%s;embedded_build_identity=%s;environment=%s;roster=%s\n' 'cargo build --release -p v1-compiler --bin claim_executor --bin gunbc' "$SOURCE_CLOSURE" 'sha256:'"$AXIS_ENVIRONMENT_DIGEST" 'claim_executor gunbc'; 'printf' 'required_lens_contract=none (a seed cargo build admits no lens contract)\n') > "$EXPECTED" - 'sha256sum' "$EXPECTED" > "$RUNNER_TEMP"'/PREIMAGE_DIGEST.out' - PREIMAGE_DIGEST=$('cut' '-d' ' ' '-f' '1' "$RUNNER_TEMP"'/PREIMAGE_DIGEST.out') - KEY='release-bins-'"$PREIMAGE_DIGEST" - 'rm' '-rf' "$ROOT"'/.release-bins-staging' - 'mkdir' '-p' "$ROOT"'/.release-bins-staging' - 'echo' 'release-bins request key '"$KEY"' over preimage:' - 'cat' "$EXPECTED" - 'echo' 'key='"$KEY" | 'tee' '-a' "$GITHUB_OUTPUT" > '/dev/null' - 'echo' 'GUNBC_MATERIALIZED_TREE_IDENTITY='"$SOURCE_CLOSURE" | 'tee' '-a' "$GITHUB_ENV" > '/dev/null' - 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 - 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) - '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/') - ('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' - 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: @@ -294,149 +65,6 @@ jobs: 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' - '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" - - 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') - STAGE="$ROOT"'/.release-bins-staging' - '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' - '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') - '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' - timeout-minutes: 5 - - name: Upload the compiler pair candidate - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: compiler-pair-candidate - path: ${{ runner.temp }}/pack.tar - if-no-files-found: error - retention-days: 1 - compression-level: 0 - 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] - timeout-minutes: 90 - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository - permissions: - contents: read - id-token: write - 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: Bind job-private native product store root - run: |- - # 🟡 dissolve-on: compiler_gate_native_cache_root_script — hand-authored two-line shell binding the job-private native product store root under RUNNER_TEMP. DISSOLVES WHEN the restore of the shared native product store is an emitted intent that names its job-private directory (a typed host-effect realization through v2.workflow.bash_emit bash_emit_program), so the mkdir and the GITHUB_ENV append are derived from that intent rather than spelled here. - mkdir -p "$RUNNER_TEMP/gunbc-native-products" - echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TEMP/gunbc-native-products" >> "$GITHUB_ENV" - if: github.event_name == 'pull_request' || github.event_name == 'merge_group' - - name: WIF auth (OIDC -> native-product-read, pull requests) - id: native_read_wif - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 - with: - workload_identity_provider: projects/582015116396/locations/global/workloadIdentityPools/github-actions/providers/github-oidc - service_account: native-product-read@gunbai-secrets.iam.gserviceaccount.com - token_format: access_token - create_credentials_file: false - if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'merge_group' - continue-on-error: true - timeout-minutes: 5 - - name: WIF auth (OIDC -> native-product-write, merge queue) - id: native_write_wif - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 - with: - workload_identity_provider: projects/582015116396/locations/global/workloadIdentityPools/github-native-product-write/providers/github-native-product-write-oidc - service_account: native-product-write@gunbai-secrets.iam.gserviceaccount.com - token_format: access_token - create_credentials_file: false - if: github.event_name == 'merge_group' - continue-on-error: true - timeout-minutes: 5 - name: Build the compiler id: build_witness_fold run: |+ @@ -491,8 +119,8 @@ jobs: env: GUNBC_NATIVE_FAULT_EXPERIMENT: ${{ github.event_name == 'pull_request' && 'skip_pull_request' || 'run' }} - WIF_ACCESS_TOKEN: ${{ steps.native_read_wif.outputs.access_token }} - GUNBC_PULL_REQUEST_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + GUNBC_NATIVE_PRODUCT_REUSE: fresh + GUNBC_MEMORY_ENVELOPE_REQUIRED: slot - name: emit and build //gunbc/instruments:v2-native-cli id: emit_build_v2_native_cli run: |+ @@ -519,435 +147,16 @@ jobs: env: GUNBC_NATIVE_FAULT_EXPERIMENT: ${{ github.event_name == 'pull_request' && 'skip_pull_request' || 'run' }} - WIF_ACCESS_TOKEN: ${{ steps.native_read_wif.outputs.access_token }} - GUNBC_PULL_REQUEST_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} - - name: Save admitted native products to the shared store - run: | - 'target/release/gunbc' 'run' '--source-root' 'dag' '--source-root' 'src/v2' '--entry' 'dag/gunbc/native_product_shared_transfer.dag' '--function' 'native_product_save_wet' - env: - WIF_ACCESS_TOKEN: ${{ steps.native_write_wif.outputs.access_token }} - if: success() && github.event_name == 'merge_group' - continue-on-error: true - timeout-minutes: 10 + GUNBC_NATIVE_PRODUCT_REUSE: fresh + GUNBC_MEMORY_ENVELOPE_REQUIRED: slot - 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.' + printf '%s\n' 'This step ran inside the fleet slot'\''s memory envelope (gunbc.runner_slot_desired: MemoryMax 23622320128 bytes, no swap once converged), which gunbc test required (GUNBC_MEMORY_ENVELOPE_REQUIRED=slot) and receipted at exit.' + printf '%s\n' 'The per-step trip at 21474836480 bytes is not asked for: a fleet job runs as the setpriv'\''d job user under a root unit (gunbc.runner_host_deploy unit_user and job_user) and memory_governor apply_memory_cgroup_bind creates its leaf at the cgroup root, so no process of a job can create or join a bounded cgroup; the first required run that asked refused in four seconds (run 38002497388, MemoryCgroupBindRefused) (gunbc.runner_slot_desired gunbc_runner_slot_job_cgroup_ownership; the climb is gunbc.guarantee_stall native_step_trip_awaits_fleet_job_cgroup_stall).' + printf '%s\n' 'An exit of 137 here is the kernel tripping the envelope'\''s memory.max: the compiler outgrew it. There is no larger runner and no retry; the line stops until the growth is understood (DESIGN section 6b).' + printf '%s\n' 'A MEMORY RECEIPT line precedes every successful subject step; a run that charged swap or was OOM-killed is refused even when its instrument held.' 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 - 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) - '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/') - ('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: - 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: Nominal witnesses (one prepared subject, one fold) - id: required_ci_measure - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - cd "$ROOT" - '\''target/release/claim_executor'\'' '\''--required-ci'\'' '\''--source-root'\'' '\''dag'\'' '\''--source-root'\'' '\''src/v2'\'' '\''--required-lane'\'' '\''witnesses'\'' '\''--measurement-receipt'\'' '\''required_ci_measurement_receipt.json'\'' - ') 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" - - if: "!cancelled() && steps.build_witness_fold.outcome == 'success'" - - name: "D0-MEASURE: seal an unreached receipt when the instrument did not return" - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'if '\''['\'' '\''!'\'' '\''-f'\'' '\''required_ci_measurement_receipt.json'\'' '\'']'\''; then if '\''['\'' "$REQUIRED_CI_INSTRUMENT_BUILD_OUTCOME" '\''='\'' '\''success'\'' '\'']'\''; then '\''target/release/claim_executor'\'' '\''--measurement-unreached-receipt'\'' '\''required_ci_measurement_receipt.json'\'' '\''--measurement-unreached-cause'\'' '\''measurement process exited before producing a receipt'\''; fi; if '\''['\'' "$REQUIRED_CI_INSTRUMENT_BUILD_OUTCOME" '\''!='\'' '\''success'\'' '\'']'\''; then '\''printf'\'' '\''%s\n'\'' '\''{"version": 1, "receipt": {"standing": "measurement_unreached", "cause": "instrument could not be built"}}'\'' > '\''required_ci_measurement_receipt.json'\''; fi; fi - ') 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" - - env: - REQUIRED_CI_INSTRUMENT_BUILD_OUTCOME: ${{ steps.build_witness_fold.outcome }} - if: always() && !cancelled() - - name: "D0-PUBLISH: upload the required CI measurement receipt" - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: required-ci-measurement-receipt - path: required_ci_measurement_receipt.json - if-no-files-found: error - retention-days: 14 - if: always() && !cancelled() - - name: "D0-ADJUDICATE: consume the published measurement receipt" - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'if '\''['\'' "$REQUIRED_CI_INSTRUMENT_BUILD_OUTCOME" '\''='\'' '\''success'\'' '\'']'\''; then '\''target/release/claim_executor'\'' '\''--adjudicate-measurement-receipt'\'' '\''required_ci_measurement_receipt.json'\''; fi - if '\''['\'' "$REQUIRED_CI_INSTRUMENT_BUILD_OUTCOME" '\''!='\'' '\''success'\'' '\'']'\''; then '\''echo'\'' '\''required-ci: adjudication REFUSED standing=measurement_unreached cause=instrument could not be built'\'' >&2; exit 1; fi - ') 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" - - env: - REQUIRED_CI_INSTRUMENT_BUILD_OUTCOME: ${{ steps.build_witness_fold.outcome }} - if: always() && !cancelled() - - name: Upload the floor's per-claim cost receipt - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: required-floor-claim-cost - path: required_floor_claim_cost.tsv - if-no-files-found: error - retention-days: 14 - if: "!cancelled() && steps.build_witness_fold.outcome == 'success'" - - name: Upload the floor's admission roster - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: required-floor-disposition - path: required_floor_disposition.tsv - if-no-files-found: error - retention-days: 14 - if: "!cancelled() && steps.build_witness_fold.outcome == 'success'" - env: - MALLOC_ARENA_MAX: "2" - GUNBC_EXPECTED_RED_ROSTER_JOIN: expected_red_roster_join.tsv - GUNBC_REQUIRED_FLOOR_DISPOSITION: required_floor_disposition.tsv - GUNBC_LONG_HOME_STORAGE_AGREEMENT: long_home_storage_agreement.tsv - GUNBC_REQUIRED_FLOOR_CLAIM_COST: required_floor_claim_cost.tsv - 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: - 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 - 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) - '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/') - ('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: - 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: Lint every target - id: lint_all_targets - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'cargo clippy --all-targets -- -D warnings') 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" - - if: "!cancelled()" - - name: "Stage0 mirrors match one emission by this seed: required CI build lane" - id: stage0_regen - run: |+ - GUNBC_FLOOR_LOG='gunbc-floor-cmd.log' - 'set' '+e' - 'set' '-o' 'pipefail' - ('sh' '-e' '-c' 'ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - cd "$ROOT" - '\''target/release/claim_executor'\'' '\''--required-ci'\'' '\''--source-root'\'' '\''dag'\'' '\''--source-root'\'' '\''src/v2'\'' '\''--required-lane'\'' '\''build'\'' - ') 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" - - if: "!cancelled() && steps.build_witness_fold.outcome == 'success'" - - name: "Registry-rostered generated artifacts (gunbc.generated_artifact): which drifted, run only after the build lane refused, to decide whether the repair applies" - id: generated_artifact_gate - run: | - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/instruments/generated_artifact_gate.dag --function main - if: "!cancelled() && github.event_name != 'merge_group' && steps.build_witness_fold.outcome == 'success' && steps.stage0_regen.outcome == 'failure'" - - name: Declare which ledger rows this repair covers - id: repair_declaration - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/instruments/generated_artifact_gate.dag --function heal_repair_declaration - if: failure() && steps.generated_artifact_gate.outcome == 'failure' && github.actor != 'dependabot[bot]' - - name: Regenerate every registry-rostered generated artifact (not the stage0 mirrors), then verify the tree it wrote - id: repair_regen - run: | - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/instruments/generated_artifact_gate.dag --function main_wet_verified - if: failure() && steps.generated_artifact_gate.outcome == 'failure' && github.actor != 'dependabot[bot]' && steps.repair_declaration.outcome == 'success' - - name: Produce the sealed repair candidate - id: repair_candidate - run: |- - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - "$ROOT/target/release/gunbc" run --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --entry dag/gunbc/heal_candidate.dag --function heal_candidate_produce_wet - env: - HEAL_REPOSITORY: ${{ github.repository }} - HEAL_PULL_NUMBER: ${{ github.event.pull_request.number }} - HEAL_HEAD_REF: ${{ github.event.pull_request.head.ref }} - HEAL_BASE_REF: ${{ github.event.pull_request.base.ref }} - HEAL_RUN_ID: ${{ github.run_id }} - if: failure() && steps.generated_artifact_gate.outcome == 'failure' && github.actor != 'dependabot[bot]' && steps.repair_declaration.outcome == 'success' && steps.repair_regen.outcome == 'success' - - name: Upload the sealed repair candidate - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f - with: - name: heal-repair-candidate - path: target/heal-repair-candidate - if-no-files-found: error - retention-days: 3 - if: failure() && steps.generated_artifact_gate.outcome == 'failure' && github.actor != 'dependabot[bot]' && steps.repair_declaration.outcome == 'success' && steps.repair_regen.outcome == 'success' && steps.repair_candidate.outcome == 'success' - env: - MALLOC_ARENA_MAX: "2" - witnesses: - runs-on: [self-hosted, linux, arm64] - needs: [rust-unit-tests, seed, emit-build, floor, generated] - timeout-minutes: 5 - if: always() - permissions: - contents: read - steps: - - name: Every required lane must have succeeded - run: |- - echo "required lanes: rust-unit-tests=$RUST_UNIT_TESTS seed=$SEED emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED (same_repo=$SAME_REPO)" - if [ "$RUST_UNIT_TESTS" = failure ] || [ "$SEED" = failure ] || [ "$EMIT_BUILD" = failure ] || [ "$FLOOR" = failure ] || [ "$GENERATED" = failure ]; then echo "::error::a required lane concluded failure (rust-unit-tests=$RUST_UNIT_TESTS seed=$SEED emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED) - open that job's log" >&2; exit 1; fi - if [ "$RUST_UNIT_TESTS" != success ]; then if [ "$GITHUB_EVENT_NAME" = pull_request ] && [ "$RUST_UNIT_TESTS" = skipped ]; then echo "::notice::the v1 unit-test lane does not run on pull_request: it runs on merge_group and workflow_dispatch on the fleet"; else echo "::error::the v1 unit-test lane produced no success (rust-unit-tests=$RUST_UNIT_TESTS), so this head carries no verdict from that lane"; exit 1; fi; 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: - RUST_UNIT_TESTS: ${{ needs['rust-unit-tests'].result }} - SEED: ${{ needs.seed.result }} - EMIT_BUILD: ${{ needs['emit-build'].result }} - FLOOR: ${{ needs.floor.result }} - GENERATED: ${{ needs.generated.result }} - SAME_REPO: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} diff --git a/.gitignore b/.gitignore index fd14ca42bc2..3f7654a2b5e 100644 --- a/.gitignore +++ b/.gitignore @@ -77,12 +77,12 @@ __pycache__/ # regenerable from committed source — symlink to target/release created by codegen /bin/ -# --- Derived failure-mode roster [owner: gunbc.recurring_failure_mode] --- -# regenerable from committed source — membership list derived from sibling row files before compile; not a second authored authority +# --- Retired failure-mode roster file [owner: gunbc.recurring_failure_mode] --- +# superseded; canonical home: the in-memory roster module contributed by source acquisition (v1_compiler.cli_run.derived_row_roster acquire_dir_files); a file at this path is a legacy artifact of an older gunbc and is refused by the loader — legacy file an older gunbc wrote on every source walk; the roster module is now derived in memory and never written /dag/gunbc/recurring_failure_mode/roster.dag -# --- Derived rung-drop roster [owner: gunbc.rung_drop] --- -# regenerable from committed source — membership list derived from sibling RungDrop row files before compile; not a second authored authority +# --- Retired rung-drop roster file [owner: gunbc.rung_drop] --- +# superseded; canonical home: the in-memory roster module contributed by source acquisition (v1_compiler.cli_run.derived_row_roster acquire_dir_files); a file at this path is a legacy artifact of an older gunbc and is refused by the loader — legacy file an older gunbc wrote on every source walk; the roster module is now derived in memory and never written /dag/gunbc/rung_drop/roster.dag # --- Generator stamp files [owner: gunbc.generated_artifact] --- diff --git a/DESIGN.md b/DESIGN.md index a8d5389b735..5e7f84ba7a4 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -67,7 +67,7 @@ THE RULE IS ABOUT LAYERS, AND COST IS ONLY ITS SYMPTOM. A claim reaches too far When two structures answer the same semantic question and one is intended to disappear, the change is a **replacement migration**, not a refinement. §2 prices an intermediate representation destined to be discarded as redundant work, and §3 forbids X and Y both answering for one fact. Editing X from its leaves upward preserves its root while every intermediate state becomes observable and load-bearing, so the migration creates more authority before it removes any. The deeper cost is that a surviving X is an **attractor**: while it stands, every nearby question is answered in its vocabulary, so decisions keep being premised on an assumption already scheduled for death — and A3 grounds agreement only on what stays stable across time, which a dead assumption by definition does not. -So the default is **delete-first**: uproot the root as early as you can, then fix forward minimally with the new solution, solving each surfaced problem from first principles rather than restoring it to its old binding. In a fail-closed substrate **the deletion is the census — but only over the population a run compiles**. Every real dependent refuses loudly, so what breaks is exactly what was load-bearing. Outside the required gate (`v2.workflow.required_floor required_gate_prefixes`) the substrate still refuses and nothing asks it to: a product-layer dependent can stop resolving, stay broken, and let every required lane report SUCCESS — so **for a root outside the gate, enumerate the consumers by name before you delete**. What cannot break loudly is covered by a declared, bounded §4b rung-drop, never by silence. Two carve-outs: a gap-intolerant boundary keeps the staged form — Y built in shadow, then one transition that switches the root and deletes X together — and where no Y can hold the boundary at all, X stays but stays **frozen**: no new investment, no new rows on its growth surfaces. +So the default is **delete-first**: uproot the root as early as you can, then fix forward minimally with the new solution, solving each surfaced problem from first principles rather than restoring it to its old binding. In a fail-closed substrate **the deletion is the census — but only over the population a run compiles**. Every real dependent refuses loudly, so what breaks is exactly what was load-bearing. Outside the required gate (`gunbc.emitted_subject_build_gate emitted_subject_build_rows`: since the 2026-10-09 ruling the one required lane compiles only the import closures of the entries those rows emit, so nearly every root is outside it) the substrate still refuses and nothing asks it to: a product-layer dependent can stop resolving, stay broken, and let every required lane report SUCCESS — so **for a root outside the gate, enumerate the consumers by name before you delete**. What cannot break loudly is covered by a declared, bounded §4b rung-drop, never by silence. Two carve-outs: a gap-intolerant boundary keeps the staged form — Y built in shadow, then one transition that switches the root and deletes X together — and where no Y can hold the boundary at all, X stays but stays **frozen**: no new investment, no new rows on its growth surfaces. **"Atomic" describes the authority transition, not the amount of implementation work.** Y has one coherent root, ordinary production never accumulates consumers that understand both representations, and X's authority ends in one motion. Y may freely reuse stable primitives, and X may serve from history as an offline differential oracle — but Y never falls back to X, resolves through it, or derives its answer from it. And the minimum Y is not the smallest thing that executes the happy path: it must preserve every required refusal, or it has erased a correctness distinction rather than completed the replacement. @@ -99,7 +99,7 @@ The domain roster is a modeled fact, not this prose: `gunbc.design_argument` `co - **external facts** — each independently versioned upstream has its own module; this is the strongest instance and was, until this section, the only one with a reviewer. Home authorities: `extdeps.external_authority::ExternalModelScope`. - **materialization / realization** — materialization and realization are separate interfaces with admissible handlers: materialization presents and stores an admitted identity and result contract; realization orders, places and transports work. This row owns whether a handler, provider or placement faithfully realizes that admitted identity and result contract (completeness, readback, retention, transport, refusal, binding obligations) without substituting, truncating or bypassing the admitted key. Cache purity is one discriminator across that boundary, not the whole domain. Key RELATION -- what relation, what scope, what complete canonical preimage, equality and collision disposition, and whether declared inputs are complete -- is owned by conformance-identity. Home authorities: `std.realization::Materialization`, `std.materialization_provider::ArtifactRequest`, `std.artifact_store::ArtifactStore`, `std.cache_interface::StorageSurface`. -- **leasing / locking / grants / privileged access** — exclusive or bounded use and privileged access are one domain: leases with a declared deadline, compare-and-set expectations, scoped authorizations, grants naming who may perform an effect and how it is released, resource handles -- and, since 2026-09-19, which AUTHORIZATION PATTERN performs a privileged effect. That pattern is a §3d selection (gunbc.auth.authorization_pattern_selection consumes std.decision over the effect's frequency, reversibility, surface, workload-identity binding, minted reach and billing consequence): workload identity federation with a per-secret cell for recurring automated effects, one operator approval through the ntfy loop for one-off or witnessed effects, a named human-only step where no API exists, and a refusal for an operator token relayed by chat or between sessions. The census (gunbc.auth.privileged_effect_census) classifies every current site by executing that selection, so the reviewer's three-valued answer is derived for the sites that exist and asked of the site a change adds. A compare-and-set slot's generation line is kept whole by file_compare_and_set and observe_cas_slot_state (gunbc.durable_cas_file_store), because several slots read it as an audit trail; a slot whose only fact is its head -- the materialization store's occupancy index and family holds are the first -- uses the distinct windowed operations (file_compare_and_set_windowed, observe_cas_slot_state_windowed, and the file_hold_*_windowed family) with a CasGenerationWindow of k >= 2, so a bounded store's own bookkeeping does not grow with its commits. They are two contracts, not one operation with a mode: keep-all callers are untouched, and only a caller that names the windowed operation can delete generations. A window read takes the head from a listing of the slot root and verifies head+1 is absent, re-reading on a race and never guessing; a write reclaims below head-k only after its own commit, and a refused delete is reported, never fatal. Exclusion is unchanged -- losers re-read and retry -- so nothing waits on a hold. docs/plans/fabric-storage.md names the head-probe bound this also lifts and assigns its remedy to compaction -- a head that starts from a snapshot; a window is that head-starting-above-1 shape for a slot whose history needs no snapshot, and the fabric heads are not opted in. Home authorities: `std.temporal_effect::HeldLease`, `std.durable_compare_and_set::CasExpectation`, `gunbc.durable_cas_file_store::file_compare_and_set_windowed`, `std.durable_exclusive_hold::DurableHoldState`, `std.scoped_authorization::ScopedAuthorization`, `std.effect_grant::Grant`, `std.resources::ResourceHandle`, `gunbc.auth.authorization_pattern_selection::select_authorization_pattern`, `gunbc.auth.github_gcp_federation::github_wif_provider`, `gunbc.auth.gcp_secret_access::SecretAccessGrant`, `gunbc.auth.access_request::AccessRequest`, `gunbc.auth.approval_capability::ApprovalCapability`, `gunbc.auth.approval_broker::redeem_capability`, `gunbc.auth.access_token_source::select_access_token_source`, `gunbc.auth.privileged_effect_census::privileged_effect_census`. +- **leasing / locking / grants / privileged access** — exclusive or bounded use and privileged access are one domain: leases with a declared deadline, compare-and-set expectations, scoped authorizations, grants naming who may perform an effect and how it is released, resource handles -- and, since 2026-09-19, which AUTHORIZATION PATTERN performs a privileged effect. That pattern is a §3d selection (gunbc.auth.authorization_pattern_selection consumes std.decision over the effect's frequency, reversibility, surface, workload-identity binding, minted reach and billing consequence): workload identity federation with a per-secret cell for recurring automated effects, one operator approval through the ntfy loop for one-off or witnessed effects, and a named human-only step where no API exists. The census (gunbc.auth.privileged_effect_census) classifies every current site by executing that selection, so the reviewer's three-valued answer is derived for the sites that exist and asked of the site a change adds. A compare-and-set slot's generation line is kept whole by file_compare_and_set and observe_cas_slot_state (gunbc.durable_cas_file_store), because several slots read it as an audit trail; a slot whose only fact is its head -- the materialization store's occupancy index and family holds are the first -- uses the distinct windowed operations (file_compare_and_set_windowed, observe_cas_slot_state_windowed, and the file_hold_*_windowed family) with a CasGenerationWindow of k >= 2, so a bounded store's own bookkeeping does not grow with its commits. They are two contracts, not one operation with a mode: keep-all callers are untouched, and only a caller that names the windowed operation can delete generations. A window read takes the head from a listing of the slot root and verifies head+1 is absent, re-reading on a race and never guessing; a write reclaims below head-k only after its own commit, and a refused delete is reported, never fatal. Exclusion is unchanged -- losers re-read and retry -- so nothing waits on a hold. docs/plans/fabric-storage.md names the head-probe bound this also lifts and assigns its remedy to compaction -- a head that starts from a snapshot; a window is that head-starting-above-1 shape for a slot whose history needs no snapshot, and the fabric heads are not opted in. Home authorities: `std.temporal_effect::HeldLease`, `std.durable_compare_and_set::CasExpectation`, `gunbc.durable_cas_file_store::file_compare_and_set_windowed`, `std.durable_exclusive_hold::DurableHoldState`, `std.scoped_authorization::ScopedAuthorization`, `std.effect_grant::Grant`, `std.resources::ResourceHandle`, `gunbc.auth.authorization_pattern_selection::select_authorization_pattern`, `gunbc.auth.github_gcp_federation::github_wif_provider`, `gunbc.auth.gcp_secret_access::SecretAccessGrant`, `gunbc.auth.access_request::AccessRequest`, `gunbc.auth.approval_capability::ApprovalCapability`, `gunbc.auth.approval_broker::redeem_capability`, `gunbc.auth.access_token_source::select_access_token_source`, `gunbc.auth.privileged_effect_census::privileged_effect_census`. - **fabric / compute** — allocation, placement and negotiation of resources. The shared authority is product.capacity (lease, pool, seat events) and product.fabric (priced selection over a provider parameter); the subjects that consume it are compute cells (work contracts, compute layouts, cell reservations, capacity class admission) and inference serving (seat binding, co-tenancy admission). Serving is INSIDE this domain, not beside it -- but the sharing is partial and the boundary is the point: both subjects reach the same priced selection fold, and each carries its OWN occupancy producer and linearization (serving: a termed LeaseGrant settled by fabric_seat_acquire over an event chain; compute: a timeless LeaseIdentity settled by file_compare_and_set). They differ further in what the lease is over -- a compute cell is stateless between grants and settles money, a serving seat is over a replica with loaded state and is quality-conditioned instead. The argument is [the serving capacity home ruling](docs/plans/serving-capacity-home-ruling.md). Home authorities: `product.fabric.selection::select_supply`, `product.capacity.lease::LeaseGrant`, `product.capacity.pool_events::SeatRequest`, `product.fabric.work::ExecutionRequirements`, `gunbc.fabric_event_log::fabric_seat_acquire`, `gunbc.compute.work_request::WorkOperation`, `gunbc.compute.work_provider_local::ComputeLayout`, `gunbc.fabric_control_plane::CellReservation`, `gunbc.fabric_executor_class::CapacityClassAdmission`, `gunbc.harness.harness_seat::harness_bind_seat`, `gunbc.serving.turn_admission::serving_group_admission`. - **decision / selection** — a choice must remain attributable to its subject, candidate field, constraints, evidence and policy, and conformance exposes accidental forks of that authority; the hard selection laws -- no answer outrunning its evidence, no front standing as a winner -- stay with the §3d reviewer, and goal assessment (std.goal_assessment) is a boundary of this row, not a member of it. Home authorities: `std.decision::DecisionSubject`, `std.decision::RealizationSelectionResult`, `std.decision::SelectionReceipt`, `std.decision::select_realization`, `std.pareto::SelectionAxis`, `std.pareto::ParetoEntry`, `std.pareto::DominanceVerdict`. - **keys / hashing** — the row's scope today is the three keying relations that have a consumed home: structural node identity (v2.std.node content_hash over std.content_hash HashFamily), the request and artifact keys of std.materialization_provider (request_key, artifact_request_key), and occurrence identity within one source graph (std.occurrence_identity) -- plus the declared-equality admissibility of a key TYPE (std.algebra algebra_profile_equality_extensional), which governs whether distinct keys of a finite map are decidably distinct (consumed by the map-literal duplicate-key check, docs/plans/map-literal-introduction-design.md). That admissibility is instantiation-blind at this rung: an opaque brand or a type parameter admits unjudged, and its next-rung trigger is instantiation-grain admission (v1 infer equality_admission_wall_note). This row owns the key RELATION -- what relation, what scope, what complete canonical preimage derives it, what equality and collision disposition govern it -- and whether the declared inputs are complete. Faithful realization of an admitted identity and result contract (completeness, readback, retention, transport, refusal, binding; cache purity as one discriminator) is owned by conformance-realization. Two further relations -- demand identity (the question asked) and the dependency read set an answer consumed -- are a declared frontier, not members: they have no home authority yet, are modeled by docs/plans/demand-engine-program.md, and enter this row when M1 of that program lands their carriers; until then a reviewer asks nothing about them under this row (DESIGN §3b: a home that owns only part of a claimed scope is a scope mismatch, so the scope is stated at what the homes own). Each member is a named keying relation within a named scope, derived rather than authored; the relation model is docs/plans/keying-relation-design.md. Home authorities: `std.content_hash::HashFamily`, `std.materialization_provider::request_key`, `v2.std.node::content_hash`, `std.occurrence_identity::OccurrenceIdAllocator`, `extdeps.realization.cache_purity::CachePurityVerdict`, `std.computation_identity::ComputationIdentity`, `std.algebra::algebra_profile_equality_extensional`. @@ -261,9 +261,9 @@ One row per class, each carrying its recognition rule and its receipts, as a fil **This section assumes you already have a built `gunbc`, and nothing here tells you how to get one — so it is not the first page for a reader who has just cloned.** That reader wants [docs/onboarding.md](docs/onboarding.md): the ordered path from a bare clone to one landed reviewed change, with every command derived from the authority that owns it (`gunbc.contributor_onboarding_path`), the obstacles attached to the step each one breaks, and a reading order for THIS document saying what is needed before a first build, what is needed before a first commit, and what can wait. That page is a projection of a path that was walked, not a substitute for it: the exit it serves is measured by an outcome, so a step there that does not work is a defect worth reporting as one. -- Three local checks, each named with the CI step that executes it — a check named here with no executing step is a decoration (§4b): `cargo clippy --all-targets -- -D warnings` (`gunbc.repo_self_build` `repo_self_clippy_command`, the only command that compiles the integration-test and example targets, so a red there is invisible to every other step) runs as the lint step of the `generated` job of `gunbc.compiler_gate_workflow` (`compiler_gate_lint_step`, operator ruling 2026-09-28: no lane the required context reads runs on a hosted runner), which runs on every pull request and on every merge_group (the queue's composed revision) and IS a required lane read by the `witnesses` aggregate — so a clippy red blocks a merge; `cargo fmt --all --check` runs in the generated pre-commit hook (`gunbc.githooks_pre_commit_emit`). `cargo test --release -p v1-compiler --lib` (`repo_self_test_command`) runs as the `rust_unit_tests` step of the fleet `rust-unit-tests` job (`gunbc.compiler_gate_workflow` `compiler_gate_unit_test_step`), a REQUIRED lane read by the `witnesses` aggregate that runs on merge_group and workflow_dispatch only, not pull_request (declared drop `gunbc.rung_drop` `rust_unit_tests_off_pull_requests`, operator 2026-10-06; the aggregate excuses its skip only on a pull_request event) — so a unit-test red blocks a merge at the queue. (From 2026-10-05 it reported without blocking under the declared drop `gunbc.rung_drop` `rust_unit_tests_over_their_cap`, because it overran its step cap on every head; it blocks again, and that drop is superseded (not retired by its trigger): the owner ruling carried by gunbc#13452 deleted the slow tests, and that loss is the declared drop `gunbc.rung_drop` `v1_integration_tests_deleted_from_the_unit_lane`.) It returned on 2026-09-30 as the blocking fleet lane the 2026-09-29 ruling (which deleted the non-blocking hosted measurement) named, bound by its runner slot's own MemoryMax so the regeneration-host tests read a real budget, and it retired the declared drop `gunbc.rung_drop` `rust_unit_tests_off_the_merge_path`. `cargo test --workspace` is local only: the test targets are compiled by the lint step and run by nobody. -- one-time per clone: `git config core.hooksPath .githooks` — the only documented manual seed; generated pre-commit/pre-push hooks then idempotently converge `merge.generated-artifact.driver` and re-assert `core.hooksPath` via argv derived from `gunbc.repo_local_git_config` (clones that skip hooksPath degrade to vanilla text-merge for generated-artifact paths; drift gate still guards at CI). The driver REFUSES rather than answering `true`: git reaches a low-level merge driver only when both sides changed the path since the merge base — measured on a four-case matrix, one-sided and identical changes never reach it — and taking the ours side there dropped the other side's authority-derived bytes with no conflict, twice on #7836 against the stage0 seed. It now leaves the ours side in the worktree with no conflict markers, marks the path unmerged, and prints the regeneration recipe; the class is mechanically preventable, not structural, and its next-rung trigger is the commit-writer binding rows in `gunbc.commit_workflow` +- Local checks, each named with what executes it — a check named here with no executing step is a decoration (§4b), and since the 2026-10-09 ruling (the v1 withdrawal, stated under **CI** below) two of the three have none. `cargo fmt --all --check` runs in the generated pre-commit hook (`gunbc.githooks_pre_commit_emit`). `cargo clippy --all-targets -- -D warnings` (`gunbc.repo_self_build` `repo_self_clippy_command`, the only command that compiles the integration-test and example targets) and `cargo test --release -p v1-compiler --lib` (`repo_self_test_command`) are LOCAL ONLY: the `generated` and `rust-unit-tests` lanes that ran them were deleted with the other v1 lanes, so a lint red or a v1 unit-test red blocks nothing. That loss is part of the declared drop `gunbc.rung_drop` `v1_required_lanes_withdrawn`. `cargo test --workspace` is local only, as it was: the test targets are compiled by nothing on the merge path and run by nobody. +- one-time per clone: `git config core.hooksPath .githooks` — the only documented manual seed; generated pre-commit/pre-push hooks then idempotently converge `merge.generated-artifact.driver` and re-assert `core.hooksPath` via argv derived from `gunbc.repo_local_git_config` (clones that skip hooksPath degrade to vanilla text-merge for generated-artifact paths, and since the 2026-10-09 ruling no CI step compares a generated artifact with its authority: `gunbc.rung_drop` `v1_required_lanes_withdrawn`). The driver REFUSES rather than answering `true`: git reaches a low-level merge driver only when both sides changed the path since the merge base — measured on a four-case matrix, one-sided and identical changes never reach it — and taking the ours side there dropped the other side's authority-derived bytes with no conflict, twice on #7836 against the stage0 seed. It now leaves the ours side in the worktree with no conflict markers, marks the path unmerged, and prints the regeneration recipe; the class is mechanically preventable, not structural, and its next-rung trigger is the commit-writer binding rows in `gunbc.commit_workflow` - **`gunbc test