From 6d9bc1ca2d5b0b06c0f00b8bcacd2fa1abb13b89 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 21:41:30 +0000 Subject: [PATCH 01/10] WIP: Fix toolchain-unresolved fabrication: reorder the cache-segment computat --- dag/gunbc/ci_workflow.dag | 16 +++++++++++++++ src/v2/workflow/ci_workflow_run_emit.dag | 25 +++++++++++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 7b7c1486b8e..373141c813a 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -33,6 +33,7 @@ import v2.workflow.ci_floor_peak_emit { } import v2.workflow.ci_workflow_run_emit { ci_isolate_toolchain_script, + ci_native_cache_root_script, ci_pin_rustup_default_script, ci_selection_control_script } @@ -109,6 +110,20 @@ fn ci_isolate_toolchain_step() -> Step { } } +fn ci_native_cache_root_step() -> Step { + RunStep { + name: Present { value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" }, + id: none, + run: ci_native_cache_root_script(), + shell: none, + env: none, + working_directory: none, + if_condition: none, + continue_on_error: none, + timeout_minutes: none + } +} + fn ci_prelude_steps() -> List { [ ci_checkout_step(), @@ -131,6 +146,7 @@ fn ci_prelude_steps() -> List { continue_on_error: none, timeout_minutes: none }, + ci_native_cache_root_step(), RunStep { name: Present { value: "Pin rustup default (isolated RUSTUP_HOME has no default toolchain)" }, id: none, diff --git a/src/v2/workflow/ci_workflow_run_emit.dag b/src/v2/workflow/ci_workflow_run_emit.dag index 3045e59fcff..f2b7210cfca 100644 --- a/src/v2/workflow/ci_workflow_run_emit.dag +++ b/src/v2/workflow/ci_workflow_run_emit.dag @@ -26,9 +26,11 @@ data ci_isolate_echo_rustup_home_command: String = "echo \"RUSTUP_HOME=$RUNNER_T data ci_isolate_echo_makeflags_command: String = "echo \"MAKEFLAGS=\" >> \"$GITHUB_ENV\"" -data ci_native_cache_root_note: String = "Durable native-artifact cache root (extdeps.realization.native_artifact_cache, PerRunnerFilesystem): RUNNER_TOOL_CACHE survives the per-run RUNNER_TEMP wipe on self-hosted runners, so emit-on-demand cargo workspaces warm across runs (compile_skipped receipts). The cached transport rebases only the declared /tmp/gunbc_ scratch prefix onto this root; content-hash path components stay the key, so cross-branch sharing on one runner is benign-by-identity. The rustc-version segment derived at prelude time remains a coarse operator-readable partition; correctness no longer depends on it because extdeps.realization.emit_on_demand_host places the host-observed resolved build-context identity (tools + constructed environment + Cargo configuration) beneath each input realization before .native_ready is consulted. Falsifier cold control: GUNBC_CI_NATIVE_CACHE_COLD_CONTROL." +data ci_native_cache_root_note: String = "Durable native-artifact cache root (extdeps.realization.native_artifact_cache, PerRunnerFilesystem): RUNNER_TOOL_CACHE survives the per-run RUNNER_TEMP wipe on self-hosted runners, so emit-on-demand cargo workspaces warm across runs (compile_skipped receipts). The cached transport rebases only the declared /tmp/gunbc_ scratch prefix onto this root; content-hash path components stay the key, so cross-branch sharing on one runner is benign-by-identity. The rustc-version segment derived after setup-rust-toolchain remains a coarse operator-readable partition; correctness no longer depends on it because extdeps.realization.emit_on_demand_host places the host-observed resolved build-context identity (tools + constructed environment + Cargo configuration) beneath each input realization before .native_ready is consulted. Fail-closed: rustc -V must succeed — a fabricated toolchain-unresolved slug would poison every native-cache write into one shared root (DESIGN 5). Falsifier cold control: GUNBC_CI_NATIVE_CACHE_COLD_CONTROL." -data ci_native_cache_root_toolchain_segment_command: String = "GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); if [ -z \"$GUNBC_TOOLCHAIN_SEG\" ]; then GUNBC_TOOLCHAIN_SEG=toolchain-unresolved; fi" +data ci_native_cache_root_toolchain_segment_assign_command: String = "GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-')" + +data ci_native_cache_root_toolchain_segment_missing_command: String = "if [ -z \"$GUNBC_TOOLCHAIN_SEG\" ]; then echo \"::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment\"; exit 1; fi" data ci_native_cache_root_mkdir_command: String = "mkdir -p \"$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG\"" @@ -89,9 +91,6 @@ fn ci_isolate_toolchain_intent(build_jobs_count: Int) -> Pipeline { ci_workflow_do(command: ci_isolate_echo_rustup_home_command), ci_workflow_do(command: ci_isolate_echo_makeflags_command), ci_workflow_do(command: ci_isolate_cargo_build_jobs_command(build_jobs_count: build_jobs_count)), - ci_workflow_do(command: ci_native_cache_root_toolchain_segment_command), - ci_workflow_do(command: ci_native_cache_root_mkdir_command), - ci_workflow_do(command: ci_native_cache_root_echo_command), ci_workflow_do(command: ci_sccache_provider_shell_injection()) ], on_failure: FailFast @@ -102,6 +101,22 @@ fn ci_isolate_toolchain_script(build_jobs_count: Int) -> String { ci_workflow_run_emit_pipeline(p: ci_isolate_toolchain_intent(build_jobs_count: build_jobs_count)) } +fn ci_native_cache_root_intent() -> Pipeline { + Pipeline { + steps: [ + ci_workflow_do(command: ci_native_cache_root_toolchain_segment_assign_command), + ci_workflow_do(command: ci_native_cache_root_toolchain_segment_missing_command), + ci_workflow_do(command: ci_native_cache_root_mkdir_command), + ci_workflow_do(command: ci_native_cache_root_echo_command) + ], + on_failure: FailFast + } +} + +fn ci_native_cache_root_script() -> String { + ci_workflow_run_emit_pipeline(p: ci_native_cache_root_intent()) +} + fn ci_pin_rustup_default_intent() -> Pipeline { Pipeline { steps: [ From ca2b51d54e838268aae70fdffd4dd53c0f276f44 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 21:44:00 +0000 Subject: [PATCH 02/10] Regenerate ci.yml and falsifier.yml for native-cache root reorder. The generated workflows now derive GUNBC_TOOLCHAIN_SEG after setup-rust-toolchain and refuse when rustc -V fails, matching the ci_workflow_run_emit authority. Co-authored-by: Cursor --- .github/workflows/ci.yml | 9 ++++++--- .github/workflows/falsifier.yml | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b29cb9dd75..ef2e4a93b6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,6 @@ jobs: echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" echo "MAKEFLAGS=" >> "$GITHUB_ENV" echo "CARGO_BUILD_JOBS=4" >> "$GITHUB_ENV" - GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then GUNBC_TOOLCHAIN_SEG=toolchain-unresolved; fi - mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" - echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" if sccache --show-stats >/dev/null 2>&1; then echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV" @@ -51,6 +48,12 @@ jobs: rustflags: '' env: HOME: ${{ runner.temp }} + - name: Derive native-cache root (rustc-version segment; after setup-rust-toolchain) + run: | + GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-') + if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" + echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) run: | rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" diff --git a/.github/workflows/falsifier.yml b/.github/workflows/falsifier.yml index f0fbaa7dcf1..005fb582a3e 100644 --- a/.github/workflows/falsifier.yml +++ b/.github/workflows/falsifier.yml @@ -27,9 +27,6 @@ jobs: echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" echo "MAKEFLAGS=" >> "$GITHUB_ENV" echo "CARGO_BUILD_JOBS=4" >> "$GITHUB_ENV" - GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then GUNBC_TOOLCHAIN_SEG=toolchain-unresolved; fi - mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" - echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" if sccache --show-stats >/dev/null 2>&1; then echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV" @@ -45,6 +42,12 @@ jobs: rustflags: '' env: HOME: ${{ runner.temp }} + - name: Derive native-cache root (rustc-version segment; after setup-rust-toolchain) + run: | + GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-') + if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" + echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) run: | rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" From 74bc2b329235a48c28be921dbb1ec1776fd34b83 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 22:09:17 +0000 Subject: [PATCH 03/10] Add discriminating witnesses for native-cache toolchain segment reorder. Witnesses refuse if toolchain-unresolved can reappear, assert isolate emission excludes segment derivation, and pin prelude ordering after setup-rust-toolchain. Co-authored-by: Cursor --- .../ci_native_cache_root_witness_test.dag | 83 +++++++++++++++++++ src/v2/workflow/ci_workflow_run_emit.dag | 15 ++++ 2 files changed, 98 insertions(+) create mode 100644 dag/test/claim/ci_native_cache_root_witness_test.dag diff --git a/dag/test/claim/ci_native_cache_root_witness_test.dag b/dag/test/claim/ci_native_cache_root_witness_test.dag new file mode 100644 index 00000000000..2eda1cb9df2 --- /dev/null +++ b/dag/test/claim/ci_native_cache_root_witness_test.dag @@ -0,0 +1,83 @@ +module test.claim.ci_native_cache_root_witness + +import gunbc.ci_workflow { ci_prelude_steps, ci_cargo_build_jobs_count } +import gunbc.ci_yaml_emit { expected_ci_yml } +import gunbc.falsifier_workflow { expected_falsifier_yml } +import v2.workflow.ci_workflow_run_emit { + ci_isolate_toolchain_script, + ci_native_cache_root_script, + ci_native_cache_fabricated_segment_literal, + ci_native_cache_segment_script_refuses_fabrication, + ci_isolate_toolchain_script_excludes_segment_derivation +} +import extdeps.github.actions { RunStep, UsesStep } +import v2.std.collection { list_at_optional } +import v2.std.optional { Present, Absent } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +data ci_native_cache_root_witness_note: String = "Discriminating witnesses for the toolchain-segment reorder+refuse fix (operator 2026-07-29): the isolate prelude must NOT derive GUNBC_TOOLCHAIN_SEG from ambient rustc (that either fabricates toolchain-unresolved or silently keys on the wrong toolchain); the dedicated native-cache step after setup-rust-toolchain must refuse on rustc -V failure. RED controls perturb the good emission with the retired fabrication literal so a regression re-opens the absorbing fallback." + +test fn witness_isolate_emission_excludes_segment_derivation() -> Bool { + ci_isolate_toolchain_script_excludes_segment_derivation( + isolate_script: ci_isolate_toolchain_script(build_jobs_count: ci_cargo_build_jobs_count()) + ) +} + +test fn witness_native_cache_root_emission_refuses_fabrication() -> Bool { + ci_native_cache_segment_script_refuses_fabrication( + script: ci_native_cache_root_script() + ) +} + +test fn witness_native_cache_root_fabrication_literal_red_control() -> Bool { + let good = ci_native_cache_root_script() + let bad = concat(good, concat("\n", concat("GUNBC_TOOLCHAIN_SEG=", ci_native_cache_fabricated_segment_literal))) + ci_native_cache_segment_script_refuses_fabrication(script: good) && + !ci_native_cache_segment_script_refuses_fabrication(script: bad) +} + +test fn witness_prelude_native_cache_root_step_follows_setup_rust() -> Bool { + match list_at_optional(xs: ci_prelude_steps(), index: 2) { + Present { value: setup_step } => + match setup_step { + UsesStep { name: Present { value: "Setup Rust" }, .. } => + match list_at_optional(xs: ci_prelude_steps(), index: 3) { + Present { value: cache_step } => + match cache_step { + RunStep { + name: Present { + value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" + }, + .. + } => true + _ => false + } + Absent => false + } + _ => false + } + Absent => false + } +} + +test fn witness_emitted_workflows_exclude_fabricated_segment_literal() -> Bool { + !string_contains(s: expected_ci_yml(), pattern: ci_native_cache_fabricated_segment_literal) && + !string_contains(s: expected_falsifier_yml(), pattern: ci_native_cache_fabricated_segment_literal) && + string_contains( + s: expected_ci_yml(), + pattern: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" + ) && + string_contains( + s: expected_falsifier_yml(), + pattern: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" + ) +} + +test fn witness_emitted_workflows_refuse_missing_segment_red_control() -> Bool { + let good = expected_ci_yml() + let bad = concat(good, ci_native_cache_fabricated_segment_literal) + !string_contains(s: good, pattern: ci_native_cache_fabricated_segment_literal) && + string_contains(s: bad, pattern: ci_native_cache_fabricated_segment_literal) +} diff --git a/src/v2/workflow/ci_workflow_run_emit.dag b/src/v2/workflow/ci_workflow_run_emit.dag index f2b7210cfca..b2ae683f9a5 100644 --- a/src/v2/workflow/ci_workflow_run_emit.dag +++ b/src/v2/workflow/ci_workflow_run_emit.dag @@ -117,6 +117,21 @@ fn ci_native_cache_root_script() -> String { ci_workflow_run_emit_pipeline(p: ci_native_cache_root_intent()) } +data ci_native_cache_fabricated_segment_literal: String = "toolchain-unresolved" + +fn ci_native_cache_segment_script_refuses_fabrication(script: String) -> Bool { + string_contains(s: script, pattern: "rustc -V") && + string_contains(s: script, pattern: "exit 1") && + !string_contains(s: script, pattern: ci_native_cache_fabricated_segment_literal) +} + +fn ci_isolate_toolchain_script_excludes_segment_derivation(isolate_script: String) -> Bool { + !string_contains(s: isolate_script, pattern: "rustc -V") && + !string_contains(s: isolate_script, pattern: "GUNBC_TOOLCHAIN_SEG") && + !string_contains(s: isolate_script, pattern: "GUNBC_NATIVE_CACHE_ROOT") && + !string_contains(s: isolate_script, pattern: ci_native_cache_fabricated_segment_literal) +} + fn ci_pin_rustup_default_intent() -> Pipeline { Pipeline { steps: [ From bbcdc406647831611142e3fe3edabaf5f7ea2a1c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 22:29:04 +0000 Subject: [PATCH 04/10] Fail closed on rustc exit status; mark native-cache shell scaffold. Check `if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V ...)` before using the segment, not just empty output from a failing rustc. Add dissolve-on scaffold marker on ci_native_cache_root_script emission per hand-shell gate (review 44669). Co-authored-by: Cursor --- .github/workflows/ci.yml | 5 ++-- .github/workflows/falsifier.yml | 5 ++-- .../ci_native_cache_root_witness_test.dag | 15 +++++++++++- src/v2/workflow/ci_workflow_run_emit.dag | 23 ++++++++++++++++--- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef2e4a93b6f..60bd0a2a88f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,8 +50,9 @@ jobs: HOME: ${{ runner.temp }} - name: Derive native-cache root (rustc-version segment; after setup-rust-toolchain) run: | - GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-') - if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + # 🟡 dissolve-on: ci_native_cache_root_script — orch-emitted foreign-executor prelude step deriving GUNBC_NATIVE_CACHE_ROOT from rustc -V after setup-rust-toolchain; leaf rustc/tr/mkdir/echo strings remain until typed toolchain probe lands on host_effect_apply (#5828 / shell→intent Phase 2) + if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V produced an empty native-cache toolchain segment after setup-rust-toolchain"; exit 1; fi mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) diff --git a/.github/workflows/falsifier.yml b/.github/workflows/falsifier.yml index 005fb582a3e..3fad51504af 100644 --- a/.github/workflows/falsifier.yml +++ b/.github/workflows/falsifier.yml @@ -44,8 +44,9 @@ jobs: HOME: ${{ runner.temp }} - name: Derive native-cache root (rustc-version segment; after setup-rust-toolchain) run: | - GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-') - if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + # 🟡 dissolve-on: ci_native_cache_root_script — orch-emitted foreign-executor prelude step deriving GUNBC_NATIVE_CACHE_ROOT from rustc -V after setup-rust-toolchain; leaf rustc/tr/mkdir/echo strings remain until typed toolchain probe lands on host_effect_apply (#5828 / shell→intent Phase 2) + if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V produced an empty native-cache toolchain segment after setup-rust-toolchain"; exit 1; fi mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) diff --git a/dag/test/claim/ci_native_cache_root_witness_test.dag b/dag/test/claim/ci_native_cache_root_witness_test.dag index 2eda1cb9df2..db3930da85c 100644 --- a/dag/test/claim/ci_native_cache_root_witness_test.dag +++ b/dag/test/claim/ci_native_cache_root_witness_test.dag @@ -8,8 +8,11 @@ import v2.workflow.ci_workflow_run_emit { ci_native_cache_root_script, ci_native_cache_fabricated_segment_literal, ci_native_cache_segment_script_refuses_fabrication, - ci_isolate_toolchain_script_excludes_segment_derivation + ci_isolate_toolchain_script_excludes_segment_derivation, + ci_native_cache_root_shell_emit_scaffold, + ci_native_cache_root_shell_emit_dissolution_trigger } +import std.disposition { Scaffold } import extdeps.github.actions { RunStep, UsesStep } import v2.std.collection { list_at_optional } import v2.std.optional { Present, Absent } @@ -75,6 +78,16 @@ test fn witness_emitted_workflows_exclude_fabricated_segment_literal() -> Bool { ) } +test fn witness_native_cache_root_emission_carries_dissolve_marker() -> Bool { + string_contains(s: ci_native_cache_root_script(), pattern: ci_native_cache_root_shell_emit_dissolution_trigger) +} + +test fn witness_native_cache_root_shell_emit_is_scaffold_holds() -> Bool { + match ci_native_cache_root_shell_emit_scaffold { + Scaffold { dissolves_to: _, bind: _ } => true + } +} + test fn witness_emitted_workflows_refuse_missing_segment_red_control() -> Bool { let good = expected_ci_yml() let bad = concat(good, ci_native_cache_fabricated_segment_literal) diff --git a/src/v2/workflow/ci_workflow_run_emit.dag b/src/v2/workflow/ci_workflow_run_emit.dag index b2ae683f9a5..1575ab60920 100644 --- a/src/v2/workflow/ci_workflow_run_emit.dag +++ b/src/v2/workflow/ci_workflow_run_emit.dag @@ -13,6 +13,8 @@ import v2.std.orchestration { } import v2.std.collection { Absent } import v2.std.diagnostic { Accepted, Rejected, None, Some } +import std.disposition { Disposition, Scaffold, SingleAuthority } +import std.decl_ref { DeclarationRef, WholeDeclaration } data ci_workflow_run_emit_refused_poison: String = "__GUNBC_ORCH_EMIT_REFUSED__ ci_workflow run-step emission was rejected by v2.compiler.emit_orchestration; this marker is not valid workflow shell, so the committed ci.yml drift gate goes red rather than a hand-spelled fallback masking the refusal (DESIGN 5: refuse, never widen)\n" @@ -28,9 +30,9 @@ data ci_isolate_echo_makeflags_command: String = "echo \"MAKEFLAGS=\" >> \"$GITH data ci_native_cache_root_note: String = "Durable native-artifact cache root (extdeps.realization.native_artifact_cache, PerRunnerFilesystem): RUNNER_TOOL_CACHE survives the per-run RUNNER_TEMP wipe on self-hosted runners, so emit-on-demand cargo workspaces warm across runs (compile_skipped receipts). The cached transport rebases only the declared /tmp/gunbc_ scratch prefix onto this root; content-hash path components stay the key, so cross-branch sharing on one runner is benign-by-identity. The rustc-version segment derived after setup-rust-toolchain remains a coarse operator-readable partition; correctness no longer depends on it because extdeps.realization.emit_on_demand_host places the host-observed resolved build-context identity (tools + constructed environment + Cargo configuration) beneath each input realization before .native_ready is consulted. Fail-closed: rustc -V must succeed — a fabricated toolchain-unresolved slug would poison every native-cache write into one shared root (DESIGN 5). Falsifier cold control: GUNBC_CI_NATIVE_CACHE_COLD_CONTROL." -data ci_native_cache_root_toolchain_segment_assign_command: String = "GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-')" +data ci_native_cache_root_toolchain_segment_assign_command: String = "if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); then echo \"::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment\"; exit 1; fi" -data ci_native_cache_root_toolchain_segment_missing_command: String = "if [ -z \"$GUNBC_TOOLCHAIN_SEG\" ]; then echo \"::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment\"; exit 1; fi" +data ci_native_cache_root_toolchain_segment_missing_command: String = "if [ -z \"$GUNBC_TOOLCHAIN_SEG\" ]; then echo \"::error::rustc -V produced an empty native-cache toolchain segment after setup-rust-toolchain\"; exit 1; fi" data ci_native_cache_root_mkdir_command: String = "mkdir -p \"$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG\"" @@ -114,13 +116,28 @@ fn ci_native_cache_root_intent() -> Pipeline { } fn ci_native_cache_root_script() -> String { - ci_workflow_run_emit_pipeline(p: ci_native_cache_root_intent()) + concat( + concat("# ", concat(ci_native_cache_root_shell_emit_dissolution_trigger, "\n")), + ci_workflow_run_emit_pipeline(p: ci_native_cache_root_intent()) + ) +} + +data ci_native_cache_root_shell_emit_scaffold: Disposition = Scaffold { + dissolves_to: SingleAuthority, + bind: DeclarationRef { + module_path: "v2.workflow.ci_workflow_run_emit", + decl_name: "ci_native_cache_root_script", + field: WholeDeclaration + } } +data ci_native_cache_root_shell_emit_dissolution_trigger: String = "🟡 dissolve-on: ci_native_cache_root_script — orch-emitted foreign-executor prelude step deriving GUNBC_NATIVE_CACHE_ROOT from rustc -V after setup-rust-toolchain; leaf rustc/tr/mkdir/echo strings remain until typed toolchain probe lands on host_effect_apply (#5828 / shell→intent Phase 2)" + data ci_native_cache_fabricated_segment_literal: String = "toolchain-unresolved" fn ci_native_cache_segment_script_refuses_fabrication(script: String) -> Bool { string_contains(s: script, pattern: "rustc -V") && + string_contains(s: script, pattern: "if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V") && string_contains(s: script, pattern: "exit 1") && !string_contains(s: script, pattern: ci_native_cache_fabricated_segment_literal) } From ae13e962da36fe63e02a86f72598e0450b46c6d6 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 22:31:25 +0000 Subject: [PATCH 05/10] Fix rustc status check: capture without pipe, then transform. The prior `if ! SEG=$(rustc -V | tr ...)` arm was inert without pipefail because the pipeline status is tr's. Under GHA bash -e, a bare capture followed by $? never reaches the check. Use `if ! RAW=$(rustc -V)` for the real status authority, transform in a separate step, keep emptiness as the post-transform edge-case refusal. Co-authored-by: Cursor --- .github/workflows/ci.yml | 3 ++- .github/workflows/falsifier.yml | 3 ++- src/v2/workflow/ci_workflow_run_emit.dag | 14 +++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60bd0a2a88f..9b4fa34328b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,8 @@ jobs: - name: Derive native-cache root (rustc-version segment; after setup-rust-toolchain) run: | # 🟡 dissolve-on: ci_native_cache_root_script — orch-emitted foreign-executor prelude step deriving GUNBC_NATIVE_CACHE_ROOT from rustc -V after setup-rust-toolchain; leaf rustc/tr/mkdir/echo strings remain until typed toolchain probe lands on host_effect_apply (#5828 / shell→intent Phase 2) - if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + if ! GUNBC_TOOLCHAIN_SEG_RAW=$(rustc -V 2>/dev/null); then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + GUNBC_TOOLCHAIN_SEG=$(echo "$GUNBC_TOOLCHAIN_SEG_RAW" | tr ' ' '-') if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V produced an empty native-cache toolchain segment after setup-rust-toolchain"; exit 1; fi mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" diff --git a/.github/workflows/falsifier.yml b/.github/workflows/falsifier.yml index 3fad51504af..38d7df9e113 100644 --- a/.github/workflows/falsifier.yml +++ b/.github/workflows/falsifier.yml @@ -45,7 +45,8 @@ jobs: - name: Derive native-cache root (rustc-version segment; after setup-rust-toolchain) run: | # 🟡 dissolve-on: ci_native_cache_root_script — orch-emitted foreign-executor prelude step deriving GUNBC_NATIVE_CACHE_ROOT from rustc -V after setup-rust-toolchain; leaf rustc/tr/mkdir/echo strings remain until typed toolchain probe lands on host_effect_apply (#5828 / shell→intent Phase 2) - if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + if ! GUNBC_TOOLCHAIN_SEG_RAW=$(rustc -V 2>/dev/null); then echo "::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment"; exit 1; fi + GUNBC_TOOLCHAIN_SEG=$(echo "$GUNBC_TOOLCHAIN_SEG_RAW" | tr ' ' '-') if [ -z "$GUNBC_TOOLCHAIN_SEG" ]; then echo "::error::rustc -V produced an empty native-cache toolchain segment after setup-rust-toolchain"; exit 1; fi mkdir -p "$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" echo "GUNBC_NATIVE_CACHE_ROOT=$RUNNER_TOOL_CACHE/gunbc-native/$GUNBC_TOOLCHAIN_SEG" >> "$GITHUB_ENV" diff --git a/src/v2/workflow/ci_workflow_run_emit.dag b/src/v2/workflow/ci_workflow_run_emit.dag index 1575ab60920..3de5e667b28 100644 --- a/src/v2/workflow/ci_workflow_run_emit.dag +++ b/src/v2/workflow/ci_workflow_run_emit.dag @@ -30,7 +30,9 @@ data ci_isolate_echo_makeflags_command: String = "echo \"MAKEFLAGS=\" >> \"$GITH data ci_native_cache_root_note: String = "Durable native-artifact cache root (extdeps.realization.native_artifact_cache, PerRunnerFilesystem): RUNNER_TOOL_CACHE survives the per-run RUNNER_TEMP wipe on self-hosted runners, so emit-on-demand cargo workspaces warm across runs (compile_skipped receipts). The cached transport rebases only the declared /tmp/gunbc_ scratch prefix onto this root; content-hash path components stay the key, so cross-branch sharing on one runner is benign-by-identity. The rustc-version segment derived after setup-rust-toolchain remains a coarse operator-readable partition; correctness no longer depends on it because extdeps.realization.emit_on_demand_host places the host-observed resolved build-context identity (tools + constructed environment + Cargo configuration) beneath each input realization before .native_ready is consulted. Fail-closed: rustc -V must succeed — a fabricated toolchain-unresolved slug would poison every native-cache write into one shared root (DESIGN 5). Falsifier cold control: GUNBC_CI_NATIVE_CACHE_COLD_CONTROL." -data ci_native_cache_root_toolchain_segment_assign_command: String = "if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V 2>/dev/null | tr ' ' '-'); then echo \"::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment\"; exit 1; fi" +data ci_native_cache_root_toolchain_segment_capture_command: String = "if ! GUNBC_TOOLCHAIN_SEG_RAW=$(rustc -V 2>/dev/null); then echo \"::error::rustc -V failed after setup-rust-toolchain: cannot derive native-cache toolchain segment\"; exit 1; fi" + +data ci_native_cache_root_toolchain_segment_transform_command: String = "GUNBC_TOOLCHAIN_SEG=$(echo \"$GUNBC_TOOLCHAIN_SEG_RAW\" | tr ' ' '-')" data ci_native_cache_root_toolchain_segment_missing_command: String = "if [ -z \"$GUNBC_TOOLCHAIN_SEG\" ]; then echo \"::error::rustc -V produced an empty native-cache toolchain segment after setup-rust-toolchain\"; exit 1; fi" @@ -106,7 +108,8 @@ fn ci_isolate_toolchain_script(build_jobs_count: Int) -> String { fn ci_native_cache_root_intent() -> Pipeline { Pipeline { steps: [ - ci_workflow_do(command: ci_native_cache_root_toolchain_segment_assign_command), + ci_workflow_do(command: ci_native_cache_root_toolchain_segment_capture_command), + ci_workflow_do(command: ci_native_cache_root_toolchain_segment_transform_command), ci_workflow_do(command: ci_native_cache_root_toolchain_segment_missing_command), ci_workflow_do(command: ci_native_cache_root_mkdir_command), ci_workflow_do(command: ci_native_cache_root_echo_command) @@ -136,10 +139,11 @@ data ci_native_cache_root_shell_emit_dissolution_trigger: String = "🟡 dissolv data ci_native_cache_fabricated_segment_literal: String = "toolchain-unresolved" fn ci_native_cache_segment_script_refuses_fabrication(script: String) -> Bool { - string_contains(s: script, pattern: "rustc -V") && - string_contains(s: script, pattern: "if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V") && + string_contains(s: script, pattern: "if ! GUNBC_TOOLCHAIN_SEG_RAW=$(rustc -V") && + string_contains(s: script, pattern: "GUNBC_TOOLCHAIN_SEG=$(echo \"$GUNBC_TOOLCHAIN_SEG_RAW\" | tr ' ' '-')") && string_contains(s: script, pattern: "exit 1") && - !string_contains(s: script, pattern: ci_native_cache_fabricated_segment_literal) + !string_contains(s: script, pattern: ci_native_cache_fabricated_segment_literal) && + !string_contains(s: script, pattern: "if ! GUNBC_TOOLCHAIN_SEG=$(rustc -V") } fn ci_isolate_toolchain_script_excludes_segment_derivation(isolate_script: String) -> Bool { From d2da7aed72873e059e289def25b55f6d0b8eb745 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 22:40:50 +0000 Subject: [PATCH 06/10] Fix witness test parse error: replace unsupported `..` rest patterns. The dag parser rejects DotDot in record patterns; expand UsesStep/RunStep matches to explicit wildcard fields so regen and heal_generated_artifacts can index the witness module. Co-authored-by: Cursor --- .../ci_native_cache_root_witness_test.dag | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/dag/test/claim/ci_native_cache_root_witness_test.dag b/dag/test/claim/ci_native_cache_root_witness_test.dag index db3930da85c..944afdacb20 100644 --- a/dag/test/claim/ci_native_cache_root_witness_test.dag +++ b/dag/test/claim/ci_native_cache_root_witness_test.dag @@ -45,7 +45,16 @@ test fn witness_prelude_native_cache_root_step_follows_setup_rust() -> Bool { match list_at_optional(xs: ci_prelude_steps(), index: 2) { Present { value: setup_step } => match setup_step { - UsesStep { name: Present { value: "Setup Rust" }, .. } => + UsesStep { + name: Present { value: "Setup Rust" }, + id: _, + uses: _, + with: _, + env: _, + if_condition: _, + continue_on_error: _, + timeout_minutes: _ + } => match list_at_optional(xs: ci_prelude_steps(), index: 3) { Present { value: cache_step } => match cache_step { @@ -53,7 +62,14 @@ test fn witness_prelude_native_cache_root_step_follows_setup_rust() -> Bool { name: Present { value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" }, - .. + id: _, + run: _, + shell: _, + env: _, + working_directory: _, + if_condition: _, + continue_on_error: _, + timeout_minutes: _ } => true _ => false } From ed6a5e3754df4f8f15bdf314192d3a408b469feb Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 22:50:19 +0000 Subject: [PATCH 07/10] WIP: Fix toolchain-unresolved fabrication: reorder the cache-segment computat --- dag/gunbc/ci_workflow.dag | 66 +++++++++++++++++++ .../ci_native_cache_root_witness_test.dag | 49 ++------------ 2 files changed, 73 insertions(+), 42 deletions(-) diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 373141c813a..995bd264c56 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -177,6 +177,72 @@ fn ci_prelude_steps() -> List { ] } +fn ci_step_is_setup_rust(s: Step) -> Bool { + match s { + UsesStep { + name: Present { value: "Setup Rust" }, + id: _, + uses: _, + with: _, + env: _, + if_condition: _, + continue_on_error: _, + timeout_minutes: _ + } => true + _ => false + } +} + +fn ci_step_is_native_cache_derive(s: Step) -> Bool { + match s { + RunStep { + name: Present { + value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" + }, + id: _, + run: _, + shell: _, + env: _, + working_directory: _, + if_condition: _, + continue_on_error: _, + timeout_minutes: _ + } => true + _ => false + } +} + +type CiPreludeOrderAcc = CiPreludeOrderAcc { seen_setup: Bool, ok: Bool, failed: Bool } + +fn ci_prelude_order_acc_advance(acc: CiPreludeOrderAcc, s: Step) -> CiPreludeOrderAcc { + match acc { + CiPreludeOrderAcc { seen_setup: _, ok: true, failed: _ } => acc + CiPreludeOrderAcc { seen_setup: _, ok: _, failed: true } => acc + CiPreludeOrderAcc { seen_setup: seen, ok: false, failed: false } => + if ci_step_is_native_cache_derive(s: s) { + if seen { + CiPreludeOrderAcc { seen_setup: true, ok: true, failed: false } + } else { + CiPreludeOrderAcc { seen_setup: seen, ok: false, failed: true } + } + } else if ci_step_is_setup_rust(s: s) { + CiPreludeOrderAcc { seen_setup: true, ok: false, failed: false } + } else { + acc + } + } +} + +fn ci_prelude_native_cache_root_follows_setup_rust() -> Bool { + match fold( + ci_prelude_steps(), + init: CiPreludeOrderAcc { seen_setup: false, ok: false, failed: false }, + f: (acc, s) => ci_prelude_order_acc_advance(acc: acc, s: s) + ) { + CiPreludeOrderAcc { seen_setup: _, ok: ok, failed: _ } => ok + } +} + data ci_artifact_consumer_prelude_note: String = "Checkout-only prelude for deploy jobs that consume the build job's release-bins artifact. Rust release-bin build + isolated rustup stay on build only — deploy verifies and runs prebuilt binaries." data ci_floor_gate_toolchain_note: String = "Batch-4 floor gates (emit_host rust smoke, self_host realized-comparison rustfmt/regen_stage0) need cargo+rustfmt on PATH. Uses setup-rust-toolchain without isolated RUSTUP_HOME — the full ci_prelude_steps isolate+pin+cache stack stays build-only (exit 127 rustup extract flake on srv3-07/08, 2026-07-21). Hosts that expose a cargo shim (e.g. /usr/local/ctrl-build-shims/cargo) without $CARGO_HOME/bin on PATH still fail bare `rustfmt` spawn inside regen_stage0 while `cargo fmt` works — ci_regen_ensure_rustfmt_path_script (v2.workflow.ci_regen_rustfmt_path_emit; If/Predicate/Exit/Let orch emit, leaf test -x / command -v / export PATH) prepends the locatable rustfmt dir onto PATH for the regen shell (receipt: CI #7290 srv3-03)." diff --git a/dag/test/claim/ci_native_cache_root_witness_test.dag b/dag/test/claim/ci_native_cache_root_witness_test.dag index 944afdacb20..d1c9e452c4e 100644 --- a/dag/test/claim/ci_native_cache_root_witness_test.dag +++ b/dag/test/claim/ci_native_cache_root_witness_test.dag @@ -1,6 +1,9 @@ module test.claim.ci_native_cache_root_witness -import gunbc.ci_workflow { ci_prelude_steps, ci_cargo_build_jobs_count } +import gunbc.ci_workflow { + ci_prelude_native_cache_root_follows_setup_rust, + ci_cargo_build_jobs_count +} import gunbc.ci_yaml_emit { expected_ci_yml } import gunbc.falsifier_workflow { expected_falsifier_yml } import v2.workflow.ci_workflow_run_emit { @@ -12,10 +15,7 @@ import v2.workflow.ci_workflow_run_emit { ci_native_cache_root_shell_emit_scaffold, ci_native_cache_root_shell_emit_dissolution_trigger } -import std.disposition { Scaffold } -import extdeps.github.actions { RunStep, UsesStep } -import v2.std.collection { list_at_optional } -import v2.std.optional { Present, Absent } +import std.disposition { Scaffold, Terminal } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly @@ -42,43 +42,7 @@ test fn witness_native_cache_root_fabrication_literal_red_control() -> Bool { } test fn witness_prelude_native_cache_root_step_follows_setup_rust() -> Bool { - match list_at_optional(xs: ci_prelude_steps(), index: 2) { - Present { value: setup_step } => - match setup_step { - UsesStep { - name: Present { value: "Setup Rust" }, - id: _, - uses: _, - with: _, - env: _, - if_condition: _, - continue_on_error: _, - timeout_minutes: _ - } => - match list_at_optional(xs: ci_prelude_steps(), index: 3) { - Present { value: cache_step } => - match cache_step { - RunStep { - name: Present { - value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" - }, - id: _, - run: _, - shell: _, - env: _, - working_directory: _, - if_condition: _, - continue_on_error: _, - timeout_minutes: _ - } => true - _ => false - } - Absent => false - } - _ => false - } - Absent => false - } + ci_prelude_native_cache_root_follows_setup_rust() } test fn witness_emitted_workflows_exclude_fabricated_segment_literal() -> Bool { @@ -101,6 +65,7 @@ test fn witness_native_cache_root_emission_carries_dissolve_marker() -> Bool { test fn witness_native_cache_root_shell_emit_is_scaffold_holds() -> Bool { match ci_native_cache_root_shell_emit_scaffold { Scaffold { dissolves_to: _, bind: _ } => true + Terminal { reason: _ } => false } } From 919d1abfc358750d5d3fcb9a0ed187fa82ba7183 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 29 Jul 2026 23:44:11 +0000 Subject: [PATCH 08/10] Retrigger CI after release-build step cold-cache timeout. The ed6a5e3754 run hit the 45-minute step limit during a cache-miss cargo compile; dag witnesses and generated-artifact drift gate are green locally. Co-authored-by: Cursor From 9ea46bb96679f3a6a7b4fdfe8eab4aed8e49442c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Jul 2026 00:33:58 +0000 Subject: [PATCH 09/10] Replace Step-coproduct predicates with yaml bridge ordering witness. Remove ci_step_is_* / CiPreludeOrderAcc fold from gunbc.ci_workflow (codex review 44779 finding 2). Prelude ordering is now pinned by a bridge substring derived from ci_setup_rust_home() and the shared step name constant, checked against expected_ci_yml/falsifier_yml. Co-authored-by: Cursor --- dag/gunbc/ci_workflow.dag | 77 +++---------------- .../ci_native_cache_root_witness_test.dag | 6 +- 2 files changed, 14 insertions(+), 69 deletions(-) diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 995bd264c56..7a1a99c3706 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -110,9 +110,11 @@ fn ci_isolate_toolchain_step() -> Step { } } +data ci_native_cache_derive_step_name: String = "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" + fn ci_native_cache_root_step() -> Step { RunStep { - name: Present { value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" }, + name: Present { value: ci_native_cache_derive_step_name }, id: none, run: ci_native_cache_root_script(), shell: none, @@ -124,6 +126,13 @@ fn ci_native_cache_root_step() -> Step { } } +fn ci_native_cache_prelude_order_bridge() -> String { + concat( + concat("HOME: ", ci_setup_rust_home()), + concat("\n - name: ", ci_native_cache_derive_step_name) + ) +} + fn ci_prelude_steps() -> List { [ ci_checkout_step(), @@ -177,72 +186,6 @@ fn ci_prelude_steps() -> List { ] } -fn ci_step_is_setup_rust(s: Step) -> Bool { - match s { - UsesStep { - name: Present { value: "Setup Rust" }, - id: _, - uses: _, - with: _, - env: _, - if_condition: _, - continue_on_error: _, - timeout_minutes: _ - } => true - _ => false - } -} - -fn ci_step_is_native_cache_derive(s: Step) -> Bool { - match s { - RunStep { - name: Present { - value: "Derive native-cache root (rustc-version segment; after setup-rust-toolchain)" - }, - id: _, - run: _, - shell: _, - env: _, - working_directory: _, - if_condition: _, - continue_on_error: _, - timeout_minutes: _ - } => true - _ => false - } -} - -type CiPreludeOrderAcc = CiPreludeOrderAcc { seen_setup: Bool, ok: Bool, failed: Bool } - -fn ci_prelude_order_acc_advance(acc: CiPreludeOrderAcc, s: Step) -> CiPreludeOrderAcc { - match acc { - CiPreludeOrderAcc { seen_setup: _, ok: true, failed: _ } => acc - CiPreludeOrderAcc { seen_setup: _, ok: _, failed: true } => acc - CiPreludeOrderAcc { seen_setup: seen, ok: false, failed: false } => - if ci_step_is_native_cache_derive(s: s) { - if seen { - CiPreludeOrderAcc { seen_setup: true, ok: true, failed: false } - } else { - CiPreludeOrderAcc { seen_setup: seen, ok: false, failed: true } - } - } else if ci_step_is_setup_rust(s: s) { - CiPreludeOrderAcc { seen_setup: true, ok: false, failed: false } - } else { - acc - } - } -} - -fn ci_prelude_native_cache_root_follows_setup_rust() -> Bool { - match fold( - ci_prelude_steps(), - init: CiPreludeOrderAcc { seen_setup: false, ok: false, failed: false }, - f: (acc, s) => ci_prelude_order_acc_advance(acc: acc, s: s) - ) { - CiPreludeOrderAcc { seen_setup: _, ok: ok, failed: _ } => ok - } -} - data ci_artifact_consumer_prelude_note: String = "Checkout-only prelude for deploy jobs that consume the build job's release-bins artifact. Rust release-bin build + isolated rustup stay on build only — deploy verifies and runs prebuilt binaries." data ci_floor_gate_toolchain_note: String = "Batch-4 floor gates (emit_host rust smoke, self_host realized-comparison rustfmt/regen_stage0) need cargo+rustfmt on PATH. Uses setup-rust-toolchain without isolated RUSTUP_HOME — the full ci_prelude_steps isolate+pin+cache stack stays build-only (exit 127 rustup extract flake on srv3-07/08, 2026-07-21). Hosts that expose a cargo shim (e.g. /usr/local/ctrl-build-shims/cargo) without $CARGO_HOME/bin on PATH still fail bare `rustfmt` spawn inside regen_stage0 while `cargo fmt` works — ci_regen_ensure_rustfmt_path_script (v2.workflow.ci_regen_rustfmt_path_emit; If/Predicate/Exit/Let orch emit, leaf test -x / command -v / export PATH) prepends the locatable rustfmt dir onto PATH for the regen shell (receipt: CI #7290 srv3-03)." diff --git a/dag/test/claim/ci_native_cache_root_witness_test.dag b/dag/test/claim/ci_native_cache_root_witness_test.dag index d1c9e452c4e..db3586c1e69 100644 --- a/dag/test/claim/ci_native_cache_root_witness_test.dag +++ b/dag/test/claim/ci_native_cache_root_witness_test.dag @@ -1,7 +1,7 @@ module test.claim.ci_native_cache_root_witness import gunbc.ci_workflow { - ci_prelude_native_cache_root_follows_setup_rust, + ci_native_cache_prelude_order_bridge, ci_cargo_build_jobs_count } import gunbc.ci_yaml_emit { expected_ci_yml } @@ -42,7 +42,9 @@ test fn witness_native_cache_root_fabrication_literal_red_control() -> Bool { } test fn witness_prelude_native_cache_root_step_follows_setup_rust() -> Bool { - ci_prelude_native_cache_root_follows_setup_rust() + let bridge = ci_native_cache_prelude_order_bridge() + string_contains(s: expected_ci_yml(), pattern: bridge) && + string_contains(s: expected_falsifier_yml(), pattern: bridge) } test fn witness_emitted_workflows_exclude_fabricated_segment_literal() -> Bool { From ec5b9f0644d187729e81b129b80ef7f1e55784d0 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Thu, 30 Jul 2026 00:42:49 +0000 Subject: [PATCH 10/10] Document prelude-order bridge as adjacency proxy with safe-direction brittleness. Record on ci_native_cache_prelude_order_bridge_note that the yaml substring witness over-constrains ordering (false-RED only) because non-fold-residue forbids the Step-coproduct match a structural predicate would need. Co-authored-by: Cursor --- dag/gunbc/ci_workflow.dag | 2 ++ dag/test/claim/ci_native_cache_root_witness_test.dag | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 7a1a99c3706..4771fe55922 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -133,6 +133,8 @@ fn ci_native_cache_prelude_order_bridge() -> String { ) } +data ci_native_cache_prelude_order_bridge_note: String = "Adjacency-as-proxy ordering witness (not a structural Step-order predicate): ci_native_cache_prelude_order_bridge builds the setup-rust HOME env line immediately followed by the native-cache step name at the emitter's six-space yaml indent. witness_prelude_native_cache_root_step_follows_setup_rust asserts expected_ci_yml/falsifier_yml contain that substring. The real invariant is only that segment derivation runs after setup-rust-toolchain installs rustc; this check over-constrains to textual adjacency and can false-RED on a legitimate step inserted between setup-rust and native-cache, emitter yaml reformatting, or setup-rust gaining fields after HOME — but not false-green if ordering regresses. A hand-rolled match on Step would be the structural alternative; non-fold-residue forbids the wildcard arms that predicate would need (receipt: nfr floor on 14bc815db4). Dissolves when prelude ordering is witnessed without coproduct-residue match arms." + fn ci_prelude_steps() -> List { [ ci_checkout_step(), diff --git a/dag/test/claim/ci_native_cache_root_witness_test.dag b/dag/test/claim/ci_native_cache_root_witness_test.dag index db3586c1e69..1424343e934 100644 --- a/dag/test/claim/ci_native_cache_root_witness_test.dag +++ b/dag/test/claim/ci_native_cache_root_witness_test.dag @@ -20,7 +20,7 @@ import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly -data ci_native_cache_root_witness_note: String = "Discriminating witnesses for the toolchain-segment reorder+refuse fix (operator 2026-07-29): the isolate prelude must NOT derive GUNBC_TOOLCHAIN_SEG from ambient rustc (that either fabricates toolchain-unresolved or silently keys on the wrong toolchain); the dedicated native-cache step after setup-rust-toolchain must refuse on rustc -V failure. RED controls perturb the good emission with the retired fabrication literal so a regression re-opens the absorbing fallback." +data ci_native_cache_root_witness_note: String = "Discriminating witnesses for the toolchain-segment reorder+refuse fix (operator 2026-07-29): the isolate prelude must NOT derive GUNBC_TOOLCHAIN_SEG from ambient rustc (that either fabricates toolchain-unresolved or silently keys on the wrong toolchain); the dedicated native-cache step after setup-rust-toolchain must refuse on rustc -V failure. RED controls perturb the good emission with the retired fabrication literal so a regression re-opens the absorbing fallback. Prelude ordering: witness_prelude_native_cache_root_step_follows_setup_rust uses ci_native_cache_prelude_order_bridge_note — adjacency proxy, safe-direction brittleness only." test fn witness_isolate_emission_excludes_segment_derivation() -> Bool { ci_isolate_toolchain_script_excludes_segment_derivation(