From de0deef500828e2e9303605b7172b4f52dec2f2c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 2 Oct 2026 21:41:55 +0000 Subject: [PATCH 1/6] Make emit-build a blocking lane on the fleet (operator sign-off 2026-10-02) Flip gunbc.compiler_gate_workflow's emit-build lane row to LaneBlocks, move the job onto the fleet runner with the fleet prelude, fork guard and skippable-on-fork arm, and regenerate witnesses.yml. Add the census roster row the now-required job owes, update the native-route rung drop's note. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/witnesses.yml | 28 ++++-- .../required_lane_resolution_census_live.dag | 18 +++- .../v2_native_route_off_the_merge_path.dag | 6 ++ dag/gunbc/witness/compiler_gate_workflow.dag | 94 +++++++++++-------- ...iler_gate_emit_build_lane_witness_test.dag | 35 +++---- 5 files changed, 112 insertions(+), 69 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 4230236a3c8..e2d4815e821 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -22,14 +22,17 @@ env: GUNBC_REQUIRED_CI_CONTRACT_EPOCH: 2026-09-01.1 jobs: emit-build: - runs-on: ubuntu-24.04-arm + runs-on: [self-hosted, linux, arm64] timeout-minutes: 90 + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository permissions: contents: read steps: - - name: Checkout + - 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: |- @@ -75,6 +78,10 @@ jobs: if (! '[' '-f' "$GUNBC_FLOOR_RECEIPT" ']') || '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']' || ('[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT"))) || ('[' "$GUNBC_FLOOR_CLASS" '=' 'none' ']' && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=structural' "$GUNBC_FLOOR_RECEIPT")) && (! ('[' '-f' "$GUNBC_FLOOR_RECEIPT" ']' && 'grep' '-q' 'class=infra' "$GUNBC_FLOOR_RECEIPT"))); then 'printf' 'class=%s\nsignature=%s\nexit=%s\n' "$GUNBC_FLOOR_CLASS" "$GUNBC_FLOOR_SIGNATURE" "$GUNBC_FLOOR_EXIT" > "$GUNBC_FLOOR_RECEIPT"; if '[' "$GUNBC_FLOOR_CLASS" '=' 'infra' ']'; then 'echo' '::error title=environment::floor_class='"$GUNBC_FLOOR_CLASS"' signature='"$GUNBC_FLOOR_SIGNATURE"' exit='"$GUNBC_FLOOR_EXIT"'; this is not a verdict about the diff. Attempt receipt: '"$GUNBC_FLOOR_RECEIPT"; fi; if '[' "$GUNBC_FLOOR_CLASS" '=' 'structural' ']'; then 'echo' '::error title=subject::floor_class='"$GUNBC_FLOOR_CLASS"' exit='"$GUNBC_FLOOR_EXIT"'; read the step log for the subject defect'; fi; fi 'exit' "$GUNBC_FLOOR_EXIT" + - name: Require isolated toolchain homes + run: | + if [ -z "${CARGO_HOME:-}" ] || [ -z "${RUSTUP_HOME:-}" ]; then echo "::error::ToolchainHomesNotIsolated CARGO_HOME=${CARGO_HOME:-unset} RUSTUP_HOME=${RUSTUP_HOME:-unset} -- the isolation step did not run, so this job shares a toolchain with every other runner slot on this host and a concurrent install can replace a binary mid-run" >&2; exit 1; fi + if: "!cancelled()" - name: emit and build //gunbc/instruments:self-host id: emit_build_self_host run: |+ @@ -125,10 +132,11 @@ jobs: - name: Why this lane is red, and what would be a real finding run: |- - printf '%s\n' 'emit-build is a NON-REQUIRED detector lane. A red here does NOT block your merge:' - printf '%s\n' 'the required context is `witnesses`, and this job is not an input to it.' + printf '%s\n' 'emit-build is a REQUIRED lane: a red here BLOCKS your merge, through the required context `witnesses`.' printf '%s\n' 'No standing break is declared, so a red here is a REAL FINDING -- most likely yours.' if: failure() + env: + MALLOC_ARENA_MAX: "2" floor: runs-on: [self-hosted, linux, arm64] timeout-minutes: 90 @@ -444,7 +452,7 @@ jobs: MALLOC_ARENA_MAX: "2" witnesses: runs-on: [self-hosted, linux, arm64] - needs: [floor, generated] + needs: [emit-build, floor, generated] timeout-minutes: 5 if: always() permissions: @@ -452,11 +460,13 @@ jobs: steps: - name: Every required lane must have succeeded run: |- - echo "required lanes: floor=$FLOOR generated=$GENERATED (same_repo=$SAME_REPO)" - if [ "$FLOOR" = failure ] || [ "$GENERATED" = failure ]; then echo "::error::a required lane concluded failure (floor=$FLOOR generated=$GENERATED) - open that job's log" >&2; exit 1; 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 witnesses fold and no generated-artifact verdict"; 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 witnesses fold and no generated-artifact verdict"; exit 1; fi; fi + echo "required lanes: emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED (same_repo=$SAME_REPO)" + if [ "$EMIT_BUILD" = failure ] || [ "$FLOOR" = failure ] || [ "$GENERATED" = failure ]; then echo "::error::a required lane concluded failure (emit-build=$EMIT_BUILD floor=$FLOOR generated=$GENERATED) - open that job's log" >&2; exit 1; fi + 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: + 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/dag/gunbc/required_lane_resolution_census_live.dag b/dag/gunbc/required_lane_resolution_census_live.dag index a76b0be27ea..c21a5c678b8 100644 --- a/dag/gunbc/required_lane_resolution_census_live.dag +++ b/dag/gunbc/required_lane_resolution_census_live.dag @@ -8,7 +8,7 @@ import v2.std.live_tree { LiveTreeDisposition, ReadsLiveTree } import extdeps.github.actions { Workflow, Job } import gunbc.compiler_gate_workflow { compiler_gate_workflow, compiler_gate_merge_path_job_ids, - compiler_gate_floor_job_id, compiler_gate_generated_job_id, compiler_gate_aggregate_job_id, + compiler_gate_emit_build_job_id, compiler_gate_floor_job_id, compiler_gate_generated_job_id, compiler_gate_aggregate_job_id, } import gunbc.ci_spec { gunbc_ci_heal_verify_target } import gunbc.ci_layer_roots { witness_layer_roots } @@ -32,6 +32,19 @@ data live_tree_disposition: LiveTreeDisposition = ReadsLiveTree // for the drift gate's entry; the floor's nominal seeds through the compiler query). fn lane_resolution_roster() -> List { [ + // emit-build became required on 2026-10-02. Its two steps run `gunbc test` over + // //gunbc/instruments:self-host and //gunbc/instruments:v2-native-cli, whose producers + // (v1_compiler.cli_run.native_lane_runner NATIVE_COMPILE_ENTRY and V2_NATIVE_CLI_ENTRY) ingest + // and resolve the import closure of these two entries before emitting it. The entry paths are + // restated from those Rust constants because no .dag row declares them yet -- a second spelling, + // so moving either entry means editing both until the producers read a .dag row. + LaneResolutionRow { + job_id: compiler_gate_emit_build_job_id, + subjects: [ + RunEntryClosure { entry_path: "src/v2/compiler/00_compile.dag" }, + RunEntryClosure { entry_path: "src/v2/cli/compile_cli.dag" }, + ] + }, LaneResolutionRow { job_id: compiler_gate_floor_job_id, subjects: [FloorNominalPreparedSubject {}] @@ -73,7 +86,8 @@ fn count_of(xs: List, x: String) -> Int { // as not blocking is read by no required context, so a module only it resolves is resolved by no // required lane, and a row for it would be the inflation this census exists to refuse. Checking // coverage against every emitted job instead refused this census outright from the day `emit-build` -// landed announced, since that lane has no row and should not have one. +// landed announced (2026-09-22), when that lane had no row and should not have had one; it has one +// since it became required (2026-10-02). fn required_job_ids(w: Workflow) -> List { let merge_path = compiler_gate_merge_path_job_ids() workflow_job_ids(w: w) |> filter(j => count_of(xs: merge_path, x: j) > 0) diff --git a/dag/gunbc/rung_drop/v2_native_route_off_the_merge_path.dag b/dag/gunbc/rung_drop/v2_native_route_off_the_merge_path.dag index 282291aa361..b22b958c876 100644 --- a/dag/gunbc/rung_drop/v2_native_route_off_the_merge_path.dag +++ b/dag/gunbc/rung_drop/v2_native_route_off_the_merge_path.dag @@ -40,6 +40,12 @@ import gunbc.guarantee_rung { Mitigatable, MechanicallyPreventable } // change that rewrote it retired only the E0573 standing: `emit-build` stays hosted and announced, // because a blocking lane may not run hosted (operator ruling 2026-09-28) and its fleet claim awaits // operator sign-off. The nightly above was deleted by #12439, which rebound its own row here. +// +// HALF OF THE TRIGGER'S PRECONDITION LANDED 2026-10-02, AND THIS ROW STANDS. The operator signed off +// the fleet claim: `emit-build`'s row is LaneBlocks and the job runs on the fleet runner. The lane +// still executes only //gunbc/instruments:self-host and //gunbc/instruments:v2-native-cli, which the +// trigger names as NOT sufficient; it does not execute //gunbc/instruments:v2-native-frontier over +// the v2.test.* universe, so no run of it can fire this trigger yet. What remains is that step. data v2_native_route_off_the_merge_path_population: List = [ "gunbc.witness_v2_native_route native_route_admission — every receipt clause, on every merge candidate", diff --git a/dag/gunbc/witness/compiler_gate_workflow.dag b/dag/gunbc/witness/compiler_gate_workflow.dag index 50b9ab71539..651905218b3 100644 --- a/dag/gunbc/witness/compiler_gate_workflow.dag +++ b/dag/gunbc/witness/compiler_gate_workflow.dag @@ -2,8 +2,8 @@ module gunbc.compiler_gate_workflow // NO LANE THE REQUIRED CONTEXT READS RUNS ON A HOSTED RUNNER (operator ruling 2026-09-28). The -// lanes the `witnesses` aggregate reads -- `floor` and `generated` -- run on the fleet, and so does -// the aggregate. The one lane that blocks nothing, `emit-build`, stays on `ubuntu-24.04-arm`. +// lanes the `witnesses` aggregate reads -- `emit-build`, `floor` and `generated` -- run on the fleet, +// and so does the aggregate. // // THE HOSTED UNIT-TEST LANE IS DELETED (operator ruling 2026-09-29). `rust-unit-tests` ran // `cargo test --release -p v1-compiler --lib` on every pull request and merge_group as a @@ -28,11 +28,14 @@ module gunbc.compiler_gate_workflow // compiler_gate_merge_path_runs_on_the_fleet refuses emission of a workflow in which a blocking // lane or the aggregate runs hosted. // -// WHY `emit-build` STAYS HOSTED. It is not read by the required context, so it cannot delay a -// landing, and its two compilations would roughly double the fleet slot-minutes a push costs, -// which is the cost the history below moved off the fleet. Its flip trigger -// (compiler_gate_lane_rows) states that turning it blocking moves it onto the fleet in the same -// change. +// `emit-build` BLOCKS, ON THE FLEET (operator sign-off 2026-10-02, given to the N7 manager as step +// 10 of the advisory program). It stayed hosted and announced while its only obstacle was the fleet +// claim its two compilations make on every pull request and merge_group; the operator agreed that +// cost, so its lane row is LaneBlocks and the job runs on the fleet runner with the fleet lanes' +// fork guard and skippable-on-fork arm. The roster did not grow: the job already existed, and only +// its standing and runner changed. It runs BESIDE `floor` rather than after it, so on the hosted +// runs before this change (whose emit-build wall was below the floor's) it adds no wall to a +// landing; the fleet wall is read from this lane's own runs, not restated here. // // SUPERSESSION CANCELS FLEET JOBS, AND THIS RULING DID NOT START THAT. The concurrency policy // cancels a running pull-request run on the next push, and the history below justified that by the @@ -79,7 +82,7 @@ import extdeps.languages.yaml.gha_workflow { project_workflow_to_yaml } import extdeps.github.actions { Workflow, Job, Step, UsesStep, RunStep, WorkflowPermissions, PermRead, - RunnerSpec, HostedRunner, SelfHosted, RunsOnExpression, Ubuntu2404Arm, + RunnerSpec, HostedRunner, SelfHosted, RunsOnExpression, github_event_name_pull_request, github_event_name_merge_group } @@ -162,8 +165,9 @@ import gunbc.emitted_subject_build_gate { import gunbc.required_ci_contract_epoch { required_ci_contract_epoch, required_ci_contract_epoch_env_key } // THE HOSTED BUILD PRELUDE. It was the `compiler` lane's whole job until the 2026-09-28 ruling deleted -// that lane (see the header); emit-build still needs the `gunbc` binary on its hosted runner, so the -// prelude outlived the lane that named it. +// that lane (see the header); gunbc.instrument_dispatch_workflow still builds `gunbc` on a hosted +// runner through it, so the prelude outlived the lane that named it. emit-build left it on +// 2026-10-02 for the fleet prelude over the same bins. data compiler_gate_required_bins: List = ["gunbc"] data compiler_gate_build_step_name: String = "Build the compiler" @@ -611,6 +615,12 @@ fn compiler_gate_floor_bound_steps() -> List { // build step keeps its id, build_witness_fold, in both jobs because every downstream condition in // each job reads that id; step ids are per job, so the two never collide. fn compiler_gate_fleet_prelude_bound_steps() -> List { + compiler_gate_fleet_prelude_bound_steps_for(bins: witness_floor_required_bins, build_step_name: compiler_gate_floor_build_step_name) +} + +// emit-build needs only `gunbc` (its instruments run through `gunbc test`), so it takes the same +// prelude over its own binaries rather than a copy of it. +fn compiler_gate_fleet_prelude_bound_steps_for(bins: List, build_step_name: String) -> List { [ CompilerGateBoundStep { step: compiler_gate_floor_checkout_step(), @@ -634,11 +644,11 @@ fn compiler_gate_fleet_prelude_bound_steps() -> List { }, CompilerGateBoundStep { step: witness_floor_build_step( - step_name: compiler_gate_floor_build_step_name, - build_script: repo_self_build_command(bins: witness_floor_required_bins), + step_name: build_step_name, + build_script: repo_self_build_command(bins: bins), ), role: consumes_only(capabilities: [CargoCapability]), - step_name: compiler_gate_floor_build_step_name, + step_name: build_step_name, }, CompilerGateBoundStep { step: witness_toolchain_homes_isolated_guard_step(), @@ -815,16 +825,15 @@ fn compiler_gate_generated_job() -> Job { // own question. The seed build is established by the fleet lanes' prelude (the `compiler` lane that // first carried it was deleted on 2026-09-28), and this lane establishes something else: that the // seed can EMIT two v2 closures and that the EMITTED crates compile. The two fail for different -// causes and a reader must see which. And the only lanes that build this tree today are the fleet's -// two blocking lanes, where these two compilations would put a non-blocking verdict's wall on the -// merge path. The prelude is paid twice and that is the price; see the runner note below for why it -// is a price worth paying here and would not be on the fleet. +// causes and a reader must see which. As its own job it runs BESIDE the floor, so its wall overlaps +// the floor's instead of adding to it; on `floor` or `generated` it would add in series. The +// prelude is paid once more per run and that is the price. // -// A HOSTED RUNNER, so this is not the standing claim on a paid runner that closed the job roster on -// 2026-09-04, and -- since the 2026-09-28 ruling -- not a dependence of the merge path on the hosted -// pool, because the lane blocks nothing (compiler_gate_lane_rows). `ubuntu-24.04-arm` is free on a -// public repository and joins no host jobserver, so a superseded pull-request run is cancelled -// rather than holding fleet capacity and leaks no ctrl-jobserver permit. THE WALL IS MEASURED ON THIS RUNNER CLASS BY THIS LANE'S OWN RUNS and is +// THE FLEET RUNNER, because the lane blocks (compiler_gate_lane_rows) and a blocking lane may not +// run hosted (compiler_gate_merge_path_runs_on_the_fleet). That makes it a standing claim on the +// fleet for every pull request and merge_group, which the operator signed off on 2026-10-02. It +// inherits the fleet lanes' exposure the header names: a superseded pull-request run is cancelled, +// and fleet_compile_cancellation_standing still reads NotSafe. THE WALL IS MEASURED ON THIS RUNNER CLASS BY THIS LANE'S OWN RUNS and is // not restated here (DESIGN section 6: name the instrument, never transcribe its output) -- the // producer is the `emit-build` job of any run of this workflow, and the two instrument steps carry // one label each so the split between the two compilations is readable from the step timings @@ -834,8 +843,7 @@ data compiler_gate_emit_build_job_id: String = "emit-build" // THE TIMEOUT IS A BOUND, NOT A BUDGET, and it is deliberately well above the measured wall: a // timeout that trims a slow-but-honest run converts a subject verdict into silence on the required // context, which is the failure `required_lanes_aggregate_job_condition` spends its whole comment -// on one module over. What keeps the lane cheap is the hosted runner and supersession, not this -// number. +// on one module over. data compiler_gate_emit_build_timeout_minutes: Int = 90 // ONE STEP PER SUBJECT, EACH WITH ITS OWN ID. `witness_floor_build_step` cannot be reused: it binds @@ -913,12 +921,12 @@ fn compiler_gate_emit_build_subject_bound_steps() -> List }) } -// THE PRELUDE IS THE HOSTED BUILD PRELUDE, WORD FOR WORD -- the deleted `compiler` lane's whole job -- -// because the subject steps need exactly what it provides: an isolated toolchain home, a pinned -// default, and the `gunbc` binary the instruments are invoked through. +// THE PRELUDE IS THE FLEET PRELUDE over `gunbc` alone, because the subject steps need exactly what it +// provides on the fleet: the judged checkout, an isolated toolchain home (and the guard requiring +// it), a pinned default, and the `gunbc` binary the instruments are invoked through. fn compiler_gate_emit_build_bound_steps() -> List { concat( - concat(compiler_gate_hosted_build_prelude_bound_steps(), compiler_gate_emit_build_subject_bound_steps()), + concat(compiler_gate_fleet_prelude_bound_steps_for(bins: compiler_gate_required_bins, build_step_name: compiler_gate_build_step_name), compiler_gate_emit_build_subject_bound_steps()), [CompilerGateBoundStep { step: compiler_gate_emit_build_notice_step(), role: capability_neutral, @@ -942,13 +950,17 @@ fn compiler_gate_emit_build_job() -> Job { Job { id: compiler_gate_emit_build_job_id, name: none, - runner: HostedRunner { label: Ubuntu2404Arm }, + runner: gunbc_ci_selected_runner_spec(), environment: none, steps: list_map(xs: compiler_gate_emit_build_bound_steps(), f: fn(b) { b.step }), needs: [], - env: none, + env: Present { + value: [ + kv(key: malloc_arena_max_env_key, value: yaml_string(s: malloc_arena_max_value)) + ] + }, outputs: none, - if_condition: none, + if_condition: Present { value: compiler_gate_floor_job_condition() }, timeout_minutes: Present { value: compiler_gate_emit_build_timeout_minutes }, continue_on_error: none, concurrency: none, @@ -1103,7 +1115,7 @@ fn compiler_gate_fork_line(row: CompilerGateLaneRow) -> String { "; then if [ \"$SAME_REPO\" = false ] && [ ", compiler_gate_var_read(row: row), " = skipped ]", "; then echo \"::notice::", n, "\"", "; else echo \"::error::", e, " (", compiler_gate_var_report(row: row), - "), so this head carries no witnesses fold and no generated-artifact verdict\"; exit 1; fi; fi" + "), so this head carries no verdict from that lane\"; exit 1; fi; fi" ], "") } } @@ -1214,11 +1226,10 @@ fn compiler_gate_aggregate_job() -> Job { // forgetting an edge -- silently, with a green gate. // // WHY THE ANSWER IS NOT "EVERY JOB MUST BE NEEDED". A lane that runs and does not block is a -// LEGITIMATE INTERIM, and this change is standing in it: `emit-build` reds on every head until -// the emitter's import gap is fixed on main, so requiring it today stops the merge queue for a -// defect it did not introduce. The operator ruling (v2 foundation manager, 2026-09-22) is to land -// the job PRESENT and NOT BLOCKING and flip it in a one-line follow-up once main's self-host -// builds. The defect is not that such a lane exists; it is that its non-blocking standing was +// LEGITIMATE INTERIM: `emit-build` stood in it from 2026-09-22 (operator ruling, v2 foundation +// manager: land it PRESENT and NOT BLOCKING while main could not self-host) until 2026-10-02, when +// the operator signed off its fleet claim and its row became LaneBlocks. No lane stands in it today; +// the arm is kept because the next lane to land may need it. The defect is not that such a lane exists; it is that its non-blocking standing was // INFERRABLE FROM AN ABSENCE, where a forgotten edge and a deliberate interim are the same bytes. // // So the standing is DECLARED and it carries the trigger that retires it -- and, since review @@ -1244,7 +1255,7 @@ type CompilerGateLaneStanding // no generated-artifact verdict for that head, and saying so is a notice rather than a refusal. // Carrying that as a row field is what lets the whole script be derived; carrying it as a // hand-written fourth line is what let the script drift from the roster. Since the 2026-09-28 ruling -// every blocking lane is a fleet lane, so today only the announced hosted lanes carry GateArmStrict, +// every blocking lane is a fleet lane, so today no live row carries GateArmStrict (emit-build left it when it moved), // and a hosted lane cannot turn blocking without moving (compiler_gate_merge_path_runs_on_the_fleet). type CompilerGateArm = GateArmStrict @@ -1262,9 +1273,10 @@ fn compiler_gate_lane_rows() -> List { CompilerGateLaneRow { job_id: compiler_gate_emit_build_job_id, var_name: "EMIT_BUILD", - arm: GateArmStrict, - standing: LaneAnnouncedNotBlocking { - flip_trigger: "OPERATOR SIGN-OFF ON THE FLEET RUNNER CLAIM. The self-host closure builds on main -- the E0573 standing is retired (gunbc.emitted_subject_build_gate emit_build_standing = EmitBuildExpectedGreen; hosted emit-build green on main c39426540c, run 36580079634) -- so what now keeps this lane non-blocking is not a red but the runner: a blocking lane may not run hosted (operator ruling 2026-09-28, compiler_gate_merge_path_runs_on_the_fleet), and moving these two compilations onto the fleet is a standing claim on it for every pull request and merge_group that the job-roster rule gives to the operator. Sufficient for: the operator agreeing the fleet cost of this job, measured from a green wall of this lane. At that point this row becomes LaneBlocks, the aggregate gains the needs edge and the variable, and the job moves onto the fleet runner with the fleet lanes' fork guard and arm, in one change." + standing: LaneBlocks, + arm: GateArmSkippableOnFork { + notice: "the emit-build lane is skipped for a fork pull request: no self-host or native-CLI emission verdict for this head", + unobserved_error: "the emit-build lane produced no success" } }, CompilerGateLaneRow { diff --git a/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag b/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag index 7b9d24cee46..0ad8f6fff13 100644 --- a/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag +++ b/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag @@ -128,13 +128,13 @@ test fn the_emit_build_lane_is_a_job_and_is_rostered() -> Bool { ) } -// AND IT DOES NOT BLOCK YET. The E0573 standing is retired, but a blocking lane may not run hosted -// (operator ruling 2026-09-28) and the fleet claim awaits operator sign-off, so the row stays -// announced. Reads the LIVE roster and aggregate, so a flip that forgets the fleet move is seen here -// as well as by compiler_gate_merge_path_runs_on_the_fleet. -test fn the_emit_build_lane_is_announced_and_not_read_by_the_gate() -> Bool { - (any(compiler_gate_blocking_job_ids(), id => id == compiler_gate_emit_build_job_id) == false) - && (any(compiler_gate_aggregate_job().needs, n => n == compiler_gate_emit_build_job_id) == false) +// AND IT BLOCKS. The E0573 standing is retired and the operator signed off the fleet claim on +// 2026-10-02, so the row is LaneBlocks. Reads the LIVE roster and aggregate, so a revert of the +// row, or a flip that forgets the fleet move (refused by compiler_gate_merge_path_runs_on_the_fleet), +// is seen here. +test fn the_emit_build_lane_blocks_and_is_read_by_the_gate() -> Bool { + any(compiler_gate_blocking_job_ids(), id => id == compiler_gate_emit_build_job_id) + && any(compiler_gate_aggregate_job().needs, n => n == compiler_gate_emit_build_job_id) } // THE ESTABLISHED LANES STILL BLOCK, NAMED RATHER THAN COUNTED. Without this, the claim above would @@ -144,10 +144,10 @@ test fn the_emit_build_lane_is_announced_and_not_read_by_the_gate() -> Bool { // have let a swapped lane through. The generated-artifact lane joined the set by the 2026-09-27 // ruling that moved it beside the floor; the hosted compiler and clippy lanes left it by the // 2026-09-28 ruling that put the merge path on the fleet (the seed build is the fleet prelude's, and -// the lint is a step on `generated`). +// the lint is a step on `generated`); emit-build joined it on the 2026-10-02 sign-off. test fn the_established_lanes_still_block() -> Bool { let blocking = compiler_gate_blocking_job_ids() - let established = [compiler_gate_floor_job_id, compiler_gate_generated_job_id] + let established = [compiler_gate_emit_build_job_id, compiler_gate_floor_job_id, compiler_gate_generated_job_id] all(established, id => any(blocking, b => b == id)) && all(blocking, b => any(established, id => id == b)) && all(blocking, id => any(compiler_gate_aggregate_job().needs, n => n == id)) @@ -185,15 +185,15 @@ test fn each_subject_is_invoked_through_gunbc_test() -> Bool { ) } -// ONE STEP PER SUBJECT ON TOP OF THE SHARED PRELUDE, and the prelude is the compiler lane's five -// steps. A lane that grew a subject without growing a step would run one compilation and report on +// ONE STEP PER SUBJECT ON TOP OF THE SHARED PRELUDE, and the prelude is the fleet prelude's six +// steps (checkout, home isolation, toolchain, pin, build, isolated-homes guard). A lane that grew a subject without growing a step would run one compilation and report on // two. -// FIVE PRELUDE STEPS, ONE PER SUBJECT, AND ONE STANDING-NOTICE STEP AT THE END. The `+ 1` was +// SIX PRELUDE STEPS, ONE PER SUBJECT, AND ONE STANDING-NOTICE STEP AT THE END. The `+ 1` was // added by the change that introduced the notice, and this claim FAILED until it was -- which is // the count doing its job rather than a number kept in step by hand. test fn the_lane_binds_one_step_per_subject_above_the_prelude() -> Bool { length(xs: compiler_gate_emit_build_bound_steps()) - == 5 + length(xs: emitted_subject_build_rows()) + 1 + == 6 + length(xs: emitted_subject_build_rows()) + 1 } // THE CARGO CONSUMER HAS A PROVIDER ORDERED BEFORE IT. This is the same closure check every other @@ -331,16 +331,17 @@ test fn the_fork_predicate_has_one_authority_across_both_transports() -> Bool { // green on main c39426540c, run 36580079634), so the red arm is // reachable only over a SUPPLIED standing. These claims render it through this probe, so the // discriminator, the no-owner rule and the retirement condition stay exercised while no red is -// declared; the live claim below asserts the green, non-blocking notice. +// declared; the live claim below asserts the green, blocking notice. fn advisory_red_probe_notice() -> String { join(emit_build_standing_notice_for(standing: EmitBuildExpectedRed { known_signature: EmittedBuildSignature { error_code: "E9999", symbol: "Probe", emitted_path: "src/probe.rs" } }, lane_blocks: false), "\n") } -test fn the_live_notice_is_green_and_not_blocking() -> Bool { +test fn the_live_notice_is_green_and_blocking() -> Bool { let text = join(compiler_gate_emit_build_notice_lines(), "\n") - string_contains(s: text, pattern: "does NOT block your merge") + string_contains(s: text, pattern: "BLOCKS your merge") + && (string_contains(s: text, pattern: "does NOT block") == false) && string_contains(s: text, pattern: "REAL FINDING") && (string_contains(s: text, pattern: "E0573") == false) && (string_contains(s: text, pattern: "CONTRADICTION") == false) @@ -417,7 +418,7 @@ test fn the_live_notice_prints_its_retirement_capability() -> Bool { // WHETHER THE LANE BLOCKS IS READ FROM THE LANE ROW, NOT WRITTEN INTO THE NOTICE. Rendered with the // status supplied both ways: a blocking lane never prints the non-blocking reassurance, and a // blocking lane with a still-Red standing prints the contradiction rather than "nothing for you to -// fix". The live half: today's row is `LaneAnnouncedNotBlocking`, so the live notice says so. +// fix". The live half: today's row is `LaneBlocks`, so the live notice says so. test fn w_RED_a_blocking_lane_never_prints_the_non_blocking_reassurance() -> Bool { let probe = EmitBuildExpectedRed { known_signature: EmittedBuildSignature { error_code: "E9999", symbol: "Probe", emitted_path: "src/probe.rs" } From 460f7155d93c5943d60248abe08a185ff7fc3091 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 2 Oct 2026 21:52:28 +0000 Subject: [PATCH 2/6] Move the emit-build census note to module-item grain (annotation inside a body refused) Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/required_lane_resolution_census_live.dag | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dag/gunbc/required_lane_resolution_census_live.dag b/dag/gunbc/required_lane_resolution_census_live.dag index c21a5c678b8..90d8737fdba 100644 --- a/dag/gunbc/required_lane_resolution_census_live.dag +++ b/dag/gunbc/required_lane_resolution_census_live.dag @@ -30,14 +30,15 @@ data live_tree_disposition: LiveTreeDisposition = ReadsLiveTree // roster does not is a refusal, never a job read as resolving nothing. The subject rows cite the // same declarations the step scripts render from (gunbc.ci.ci_spec gunbc_ci_heal_verify_target // for the drift gate's entry; the floor's nominal seeds through the compiler query). +// +// emit-build became required on 2026-10-02. Its two steps run `gunbc test` over +// //gunbc/instruments:self-host and //gunbc/instruments:v2-native-cli, whose producers +// (v1_compiler.cli_run.native_lane_runner NATIVE_COMPILE_ENTRY and V2_NATIVE_CLI_ENTRY) ingest +// and resolve the import closure of these two entries before emitting it. The entry paths are +// restated from those Rust constants because no .dag row declares them yet -- a second spelling, +// so moving either entry means editing both until the producers read a .dag row. fn lane_resolution_roster() -> List { [ - // emit-build became required on 2026-10-02. Its two steps run `gunbc test` over - // //gunbc/instruments:self-host and //gunbc/instruments:v2-native-cli, whose producers - // (v1_compiler.cli_run.native_lane_runner NATIVE_COMPILE_ENTRY and V2_NATIVE_CLI_ENTRY) ingest - // and resolve the import closure of these two entries before emitting it. The entry paths are - // restated from those Rust constants because no .dag row declares them yet -- a second spelling, - // so moving either entry means editing both until the producers read a .dag row. LaneResolutionRow { job_id: compiler_gate_emit_build_job_id, subjects: [ From 27a38105ea5e352311ed90adabe57f5af2b3aa43 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sat, 3 Oct 2026 00:01:31 +0000 Subject: [PATCH 3/6] THROWAWAY: break the seed emission route used only by emit-build; do not merge --- src/v1/stage0/src/cli_run/native_lane_runner.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/v1/stage0/src/cli_run/native_lane_runner.rs b/src/v1/stage0/src/cli_run/native_lane_runner.rs index c3e22180b61..cbd564afc66 100644 --- a/src/v1/stage0/src/cli_run/native_lane_runner.rs +++ b/src/v1/stage0/src/cli_run/native_lane_runner.rs @@ -309,6 +309,12 @@ fn emitted_closure_identity(crate_dir: &Path) -> Result { /// compiler" is. The seed is used exactly once here, in-process, to emit; the receipt records /// the seed's identity honestly, and this job claims no native bootstrap. fn prepare_emitted_compiler(source_roots: &[String]) -> Result { + if !source_roots.is_empty() { + return Err( + "THROWAWAY gunbc#13020: emission deliberately broken (discriminating red for #13018)" + .to_string(), + ); + } prepare_emitted_compiler_for_entry(source_roots, NATIVE_COMPILE_ENTRY) } From 99656efe61703c9b821e4203272e0da4038ea69e Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sat, 3 Oct 2026 00:02:03 +0000 Subject: [PATCH 4/6] Revert the throwaway emission break committed to this branch by mistake Co-Authored-By: Claude Opus 5.5 (1M context) --- src/v1/stage0/src/cli_run/native_lane_runner.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/v1/stage0/src/cli_run/native_lane_runner.rs b/src/v1/stage0/src/cli_run/native_lane_runner.rs index cbd564afc66..c3e22180b61 100644 --- a/src/v1/stage0/src/cli_run/native_lane_runner.rs +++ b/src/v1/stage0/src/cli_run/native_lane_runner.rs @@ -309,12 +309,6 @@ fn emitted_closure_identity(crate_dir: &Path) -> Result { /// compiler" is. The seed is used exactly once here, in-process, to emit; the receipt records /// the seed's identity honestly, and this job claims no native bootstrap. fn prepare_emitted_compiler(source_roots: &[String]) -> Result { - if !source_roots.is_empty() { - return Err( - "THROWAWAY gunbc#13020: emission deliberately broken (discriminating red for #13018)" - .to_string(), - ); - } prepare_emitted_compiler_for_entry(source_roots, NATIVE_COMPILE_ENTRY) } From fbbb190d0d80300feec5725e03dddfe77dd88284 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 17:09:18 +0000 Subject: [PATCH 5/6] Serve the live compiler-gate job ids warm; retire emit-build from the announced-lane claim The emit-build-is-a-job inhabitance claim refused COMPLETED-OVER-COST-REQUIREMENT (84850 marginal eval steps, floor run 37213019300) once emit-build moved onto the fleet prelude. Both live claims that read compiler_gate_non_aggregate_job_ids now share one warm evaluation of it, and still read the live workflow. w_RED_the_live_announced_lanes_reach_neither_surface asserted EMIT_BUILD absent from the gate, which this PR makes false (local claim_batch: FAIL). emit-build now blocks; the claim keeps the deleted unit-test lane's absence and is renamed for it. Co-Authored-By: Claude Opus 5.5 (1M context) --- ...ompiler_gate_emit_build_lane_witness_test.dag | 16 ++++++++-------- src/v2/workflow/floor_pure_producer_share.dag | 10 +++++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag b/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag index 0ad8f6fff13..bb09de72863 100644 --- a/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag +++ b/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag @@ -267,16 +267,16 @@ test fn w_RED_an_announced_lane_binds_no_variable_and_is_read_by_no_clause() -> && string_contains(s: blocking_script, pattern: "[ \"$PROBE_LANE\" != success ]") } -// AND THE ANNOUNCED LANES REACH NEITHER SURFACE. Without this the two claims above would be -// satisfied by a gate that also read a lane it must not yet block on -- whose variable would -// render EMPTY, compare unequal to `success`, and refuse every run. Both announced lanes are named: -// emit-build, and since 2026-09-28 the unit-test lane. -test fn w_RED_the_live_announced_lanes_reach_neither_surface() -> Bool { +// AND THE DELETED UNIT-TEST LANE REACHES NEITHER SURFACE. Without this the gate could read a lane +// that no longer runs -- whose variable would render EMPTY, compare unequal to `success`, and refuse +// every run. emit-build left this claim on the 2026-10-02 sign-off: it now BLOCKS, and +// the_emit_build_lane_blocks_and_is_read_by_the_gate asserts the opposite of what this claim +// asserted about it. No live lane is announced today, so the announced-lane mechanism itself is held by +// the supplied-row claim w_RED_an_announced_lane_binds_no_variable_and_is_read_by_no_clause. +test fn w_RED_the_deleted_unit_test_lane_reaches_neither_surface() -> Bool { let script = join(compiler_gate_aggregate_script_lines(), "\n") let env = compiler_gate_aggregate_step_env() - (any(env, e => e.key == "EMIT_BUILD") == false) - && (string_contains(s: script, pattern: "EMIT_BUILD") == false) - && (any(env, e => e.key == "RUST_UNIT_TESTS") == false) + (any(env, e => e.key == "RUST_UNIT_TESTS") == false) && (string_contains(s: script, pattern: "RUST_UNIT_TESTS") == false) } diff --git a/src/v2/workflow/floor_pure_producer_share.dag b/src/v2/workflow/floor_pure_producer_share.dag index 907b6f2183d..671a5e551e5 100644 --- a/src/v2/workflow/floor_pure_producer_share.dag +++ b/src/v2/workflow/floor_pure_producer_share.dag @@ -851,6 +851,13 @@ import v2.std.algebra { any } // eval-step cap. The rows now read five nullary producers, one per program -- the map-over-records program's // producer, a record of three Bools, serving three rows -- and the others a Symbol and three Bools. WARM: one // assembly is more than one claim's budget. +// THE LIVE COMPILER-GATE JOB IDS ARE DERIVED ONCE. gunbc.compiler_gate_workflow +// compiler_gate_non_aggregate_job_ids maps the whole live Workflow's jobs to their ids; two claims read it in +// test.claim.compiler_gate_emit_build_lane_witness_test (the live standings join and the emit-build-is-a-job +// inhabitance claim) and the emission conjunct reads it too. gunbc#13018's floor (run 37213019300) refused the +// inhabitance claim COMPLETED-OVER-COST-REQUIREMENT at 84850 marginal eval steps once emit-build moved onto the +// fleet prelude. Its value is a list of Strings. WARM: the live workflow is built once, outside the fold, and both +// claims still read the LIVE value rather than a supplied one. data floor_cross_claim_pure_producers_warm: List = [ "v2.test.claim.parameter_reference.pr_program_parameter_paths", "v2.test.claim.parameter_reference.pr_program_grounding", @@ -1521,7 +1528,8 @@ data floor_cross_claim_pure_producers_warm: List = [ "v2.test.claim.compiler.collection_callback_realization.ccr_map_undeclared_field_reason", "v2.test.claim.compiler.collection_callback_realization.ccr_piped_map_binds_its_row", "v2.test.claim.compiler.collection_callback_realization.ccr_all_binds_its_row", - "v2.test.claim.compiler.collection_callback_realization.ccr_declared_any_lowers_no_loop" + "v2.test.claim.compiler.collection_callback_realization.ccr_declared_any_lowers_no_loop", + "gunbc.compiler_gate_workflow.compiler_gate_non_aggregate_job_ids" ] // grammar_relation_row_for_emitted HAS NOW BEEN MEASURED ON THE THIRD CONJUNCT, AND IT PASSES. From 0d71014cc7ad175af240c4d959d9b3c7659b39a2 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 22:11:00 +0000 Subject: [PATCH 6/6] emit-build census entries read from the instrument rows, not copied Review 75874: lane_resolution_roster's emit-build row hand-copied the two entry paths from the seed's native_lane_runner constants, a second authority that could go stale silently. The entries are now the instruments' own facts, gunbc.instrument_targets self_host_entry / v2_native_cli_entry, which the census reads. The seed's Rust constants cannot read a .dag row yet, so test.claim.compiler_gate_emit_build_lane_witness_test the_seed_lane_entries_are_the_instrument_rows joins each constant's whole declaration line to its row and reds on drift (mutating V2_NATIVE_CLI_ENTRY alone turns it FAIL; 37 eval steps). Co-Authored-By: Claude Opus 5.5 (1M context) --- dag/gunbc/instrument_targets.dag | 12 +++++++++++ .../required_lane_resolution_census_live.dag | 12 ++++++----- ...iler_gate_emit_build_lane_witness_test.dag | 20 +++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/dag/gunbc/instrument_targets.dag b/dag/gunbc/instrument_targets.dag index 8d47129963c..8468ec1bbfa 100644 --- a/dag/gunbc/instrument_targets.dag +++ b/dag/gunbc/instrument_targets.dag @@ -227,6 +227,18 @@ fn v2_native_cli_label() -> Label { Label { package: instruments_package() target: TargetName { name: "v2-native-cli" } } } +// THE ENTRY EACH OF THE TWO SELF-HOST INSTRUMENTS EMITS, AS THE INSTRUMENT'S OWN FACT. Every +// reader of these entries -- the required-lane resolution census, and the seed's native lane runner +// whose constants test.claim.compiler_gate_emit_build_lane_witness_test holds against these rows -- +// takes them from here, so a moved subject cannot leave a stale closure reported as covered. +fn self_host_entry() -> String { + "src/v2/compiler/00_compile.dag" +} + +fn v2_native_cli_entry() -> String { + "src/v2/cli/compile_cli.dag" +} + // THE NATIVE FRONTIER INSTRUMENT: the same adjudicating run as the native route, over the whole // v2.test.* universe, answered by gunbc.native_frontier_ratchet instead of by the lane's route // qualification. The two are different questions. Qualification asks whether this run was a sound diff --git a/dag/gunbc/required_lane_resolution_census_live.dag b/dag/gunbc/required_lane_resolution_census_live.dag index 90d8737fdba..617150cde4f 100644 --- a/dag/gunbc/required_lane_resolution_census_live.dag +++ b/dag/gunbc/required_lane_resolution_census_live.dag @@ -11,6 +11,7 @@ import gunbc.compiler_gate_workflow { compiler_gate_emit_build_job_id, compiler_gate_floor_job_id, compiler_gate_generated_job_id, compiler_gate_aggregate_job_id, } import gunbc.ci_spec { gunbc_ci_heal_verify_target } +import gunbc.instrument_targets { self_host_entry, v2_native_cli_entry } import gunbc.ci_layer_roots { witness_layer_roots } import gunbc.required_lane_resolution_census { ModuleIdentityPopulation, ModuleIdentityPopulationObserved, ModuleIdentityPopulationRefused, @@ -34,16 +35,17 @@ data live_tree_disposition: LiveTreeDisposition = ReadsLiveTree // emit-build became required on 2026-10-02. Its two steps run `gunbc test` over // //gunbc/instruments:self-host and //gunbc/instruments:v2-native-cli, whose producers // (v1_compiler.cli_run.native_lane_runner NATIVE_COMPILE_ENTRY and V2_NATIVE_CLI_ENTRY) ingest -// and resolve the import closure of these two entries before emitting it. The entry paths are -// restated from those Rust constants because no .dag row declares them yet -- a second spelling, -// so moving either entry means editing both until the producers read a .dag row. +// and resolve the import closure of these two entries before emitting it. The entries are the +// instruments' own facts in gunbc.instrument_targets (self_host_entry, v2_native_cli_entry); the +// seed's two Rust constants cannot read a .dag row yet, so +// test.claim.compiler_gate_emit_build_lane_witness_test holds them equal to those rows and reds on drift. fn lane_resolution_roster() -> List { [ LaneResolutionRow { job_id: compiler_gate_emit_build_job_id, subjects: [ - RunEntryClosure { entry_path: "src/v2/compiler/00_compile.dag" }, - RunEntryClosure { entry_path: "src/v2/cli/compile_cli.dag" }, + RunEntryClosure { entry_path: self_host_entry() }, + RunEntryClosure { entry_path: v2_native_cli_entry() }, ] }, LaneResolutionRow { diff --git a/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag b/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag index bb09de72863..d31b0547a26 100644 --- a/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag +++ b/dag/test/claim/compiler_gate_emit_build_lane_witness_test.dag @@ -32,6 +32,8 @@ import gunbc.emitted_subject_build_gate { } import extdeps.exec.command { shell_command_render } import v2.std.algebra { length, filter } +import extdeps.filesystem.filesystem_io +import gunbc.instrument_targets { self_host_entry, v2_native_cli_entry } // WHAT THIS FILE DISCRIMINATES. Three facts, and each has a red that is authorable HERE rather // than only by a future edit to the workflow: the aggregate-enrollment predicate refuses a lane @@ -465,3 +467,21 @@ test fn the_notice_is_the_last_step_in_the_lane() -> Bool { f: (acc, b) => b.step_name ) == "Why this lane is red, and what would be a real finding" } + +// THE SEED'S TWO LANE ENTRIES AGREE WITH THE INSTRUMENT ROWS. The emit-build lane's census subject is +// read from gunbc.instrument_targets, while the producer that actually emits is the seed's native +// lane runner, whose entries are Rust constants that cannot read a .dag row yet. This joins them, so +// moving either entry on one side alone reds here rather than leaving the census reporting a closure +// the lane no longer compiles. Each constant is matched as its whole declaration line, so the join +// pins the NAME to the VALUE, not two substrings that could come from different lines. +data native_lane_runner_source_path: String = "src/v1/stage0/src/cli_run/native_lane_runner.rs" + +fn rust_str_const_line(name: String, value: String) -> String { + join(["const ", name, ": &str = \"", value, "\";"], "") +} + +test fn the_seed_lane_entries_are_the_instrument_rows() -> Bool { + let runner = Filesystem.Read(path: native_lane_runner_source_path, expect: ExpectSuccess).content + string_contains(s: runner, pattern: rust_str_const_line(name: "NATIVE_COMPILE_ENTRY", value: self_host_entry())) + && string_contains(s: runner, pattern: rust_str_const_line(name: "V2_NATIVE_CLI_ENTRY", value: v2_native_cli_entry())) +}