From d20b899f800e2fa21a5e1fd4e4a5ca2c84ffb4d6 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 10 Sep 2026 23:16:21 +0000 Subject: [PATCH 01/27] Make required-v2-native's envelope, flags, budget, and main occupancy truthful. The merged native job still declared a 180-minute timeout and a unique-per-push concurrency key while the route's own census is the producer for a multi-hour wall on shared arm64 runners. Raise the job timeout from the workflow authority, serialize main onto github.ref without cancelling in-flight runs, set -D warnings on the emitted crate's cargo, and print HostBudget peak against the slot cgroup on the receipt. Co-authored-by: Cursor --- .github/workflows/witnesses.yml | 4 +- .../fleet_desired_admission_workflow.dag | 9 +- dag/gunbc/floor/floor_attempt_standing.dag | 7 +- dag/gunbc/repo/repo_ruleset.dag | 15 +- dag/gunbc/witness/v2_native_route.dag | 25 +++ dag/gunbc/witness/witness_floor_workflow.dag | 84 +++++----- dag/test/claim/repo_ruleset_witness_test.dag | 7 +- .../claim/run_disposition_witness_test.dag | 16 +- ...or_workflow_consolidation_witness_test.dag | 16 +- .../stage0/src/cli_run/native_lane_runner.rs | 143 +++++++++++++++++- .../src/emitted_closure_compile_host.rs | 76 ++++++++-- src/v2/test/v2_native_route_test.dag | 67 +++++--- 12 files changed, 367 insertions(+), 102 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index e0b45318b6f..03957839178 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -17,7 +17,7 @@ on: permissions: contents: read concurrency: - group: witness-floor-${{ github.event.pull_request.number || github.run_id }} + group: witness-floor-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: false env: CARGO_TERM_COLOR: always @@ -405,7 +405,7 @@ jobs: if: always() && steps.heal_commit_push.outputs.heal_author_commit_required == '1' required-v2-native: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 180 + timeout-minutes: 360 steps: - name: Record runner filesystem at job start run: | diff --git a/dag/gunbc/fleet/fleet_desired_admission_workflow.dag b/dag/gunbc/fleet/fleet_desired_admission_workflow.dag index 6241cfe617b..82077cbb53a 100644 --- a/dag/gunbc/fleet/fleet_desired_admission_workflow.dag +++ b/dag/gunbc/fleet/fleet_desired_admission_workflow.dag @@ -325,9 +325,12 @@ fn fleet_desired_admission_job() -> Job { // WHAT ORDERING DOES NOT COVER: THE DESIRED REF CAN MOVE BACKWARD, AND THIS IS ORDINARY RATHER // THAN AN EDGE CASE. An earlier revision of this annotation said reaching it "requires deliberately // re-running an old floor run". THAT UNDERSTATED IT AND WAS WRONG. The floor's own concurrency -// group is `witness-floor-${{ github.event.pull_request.number || github.run_id }}`, and on a -// default-branch push there is no pull-request number, so the group is unique per run -- the floor -// imposes NO ordering between main pushes. Two merges therefore race by construction: +// group is `witness-floor-${{ github.event.pull_request.number || github.ref }}`, and on a +// default-branch push there is no pull-request number, so the group is the branch ref -- main +// runs SERIALIZE. An in-flight main run is allowed to finish (`NeverSupersedeRunning`); a +// not-yet-started same-group run is replaced. Two merges therefore no longer start two native +// jobs at once. The residual race is a finishing older in-flight admission versus a later +// successor that started after it: // // floor(C1) starts; floor(C2) starts // floor(C2) finishes first -> admission advances D -> C2 diff --git a/dag/gunbc/floor/floor_attempt_standing.dag b/dag/gunbc/floor/floor_attempt_standing.dag index 5346526edca..45a978985bf 100644 --- a/dag/gunbc/floor/floor_attempt_standing.dag +++ b/dag/gunbc/floor/floor_attempt_standing.dag @@ -549,9 +549,10 @@ fn floor_rendered_verdict_name(verdict: FloorRenderedVerdict) -> String { // ACQUISITION rather than of this composition: nothing in the required run reads the pull // request's current head, so no two observations can disagree. The census behind that statement: // no step in any emitted workflow shells to `gh api`, `gh run` or `gh pr` or curls the API; the -// only run-metadata expression anywhere is `github.run_id` inside the witnesses concurrency key, -// which is a key string and not an observation; and `witnesses.yml` grants `contents: read` with -// no token in any job's env, so no such step could authenticate if it existed. +// only run-metadata expressions in the emitted workflow are `github.ref` inside the witnesses +// concurrency key (a key string, not an observation), `github.run_id` on the aggregate job's +// env, and the usual checkout `github.sha` interpolations; and `witnesses.yml` grants +// `contents: read` with no token in any job's env, so no such step could authenticate if it existed. // // THE SHAPE IS NOT NET-NEW, ONLY ITS ACQUISITION HERE IS. `extdeps.github.pulls` declares // `github.Pulls.Get` over a rest transport returning `PullRequest`, whose `head` carries `sha`, diff --git a/dag/gunbc/repo/repo_ruleset.dag b/dag/gunbc/repo/repo_ruleset.dag index eb930a631e4..0be3f738dcc 100644 --- a/dag/gunbc/repo/repo_ruleset.dag +++ b/dag/gunbc/repo/repo_ruleset.dag @@ -5,7 +5,7 @@ import std.types { HttpStatus, NonEmptyStr } import std.dissolution { DissolutionCondition, unbound_dissolution } import std.realization_reconcile { Reconciliation, Converged, NotConverged } import gunbc.repository { gunbc_repository } -import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, witness_floor_lane_timeout } +import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, required_ci_slowest_lane_timeout } import std.measure { Minute, minute, minute_count, measure_add, MergeQueueEntryCount, merge_queue_entry_count, merge_queue_entry_count_value } import extdeps.github.actions { github_actions_app_integration_id } import extdeps.github.rulesets @@ -264,14 +264,15 @@ type MergeQueueDesire // them. SQUASH is what keeps the queue's publication the same shape as the one this repository // already documents. // -// check_response_timeout IS DERIVED FROM THE FLOOR'S OWN ENVELOPE AND IS NOT A CONSTANT HERE. It -// reads gunbc.witness_floor_workflow witness_floor_lane_timeout and adds a declared margin, both as Minute. +// check_response_timeout IS DERIVED FROM THE REQUIRED AGGREGATE'S SLOWEST ENVELOPE AND IS NOT A +// CONSTANT HERE. It reads gunbc.witness_floor_workflow required_ci_slowest_lane_timeout (the +// max of the floor lane and the v2-native lane) and adds a declared margin, both as Minute. // A queue whose response timeout is BELOW the repository's execution envelope manufactures queue // failures out of runs that were going to pass: it dequeues green work for being slow, and the // author of the next timeout bump would have no way to know a second number tracked the first. -// Transcribing 180 here would have been exactly that second authority, so the number is read from -// the row that owns it -- and the witness asserts the ordering rather than the value, so raising -// the floor's timeout cannot silently invert it. +// Transcribing a minute count here would have been exactly that second authority, so the +// number is read from the row that owns it -- and the witness asserts the ordering rather than +// the value, so raising a required job's timeout cannot silently invert it. // // THE ENTRY COUNTS ARE COMMISSIONING VALUES, NOT THROUGHPUT VALUES, and the difference is the // whole reason they are 1. Merging one at a time means a failure names one pull request rather @@ -294,7 +295,7 @@ data merge_queue_build_concurrency_increase: DissolutionCondition = unbound_diss data merge_queue_response_margin: Minute = minute(count: 30) fn merge_queue_check_response_timeout() -> Minute { - measure_add(a: witness_floor_lane_timeout, b: merge_queue_response_margin) + measure_add(a: required_ci_slowest_lane_timeout(), b: merge_queue_response_margin) } // THE CONTENTION THIS PROPOSAL TARGETS IS MEASURED, NOT ARGUED FROM FIRST PRINCIPLES, AND THE diff --git a/dag/gunbc/witness/v2_native_route.dag b/dag/gunbc/witness/v2_native_route.dag index b0336d076e9..4b2ddf023a2 100644 --- a/dag/gunbc/witness/v2_native_route.dag +++ b/dag/gunbc/witness/v2_native_route.dag @@ -47,6 +47,13 @@ import v2.workflow.floor_route_gap { floor_route_gap_roster } // all cite this declaration; no second spelling of the job name anywhere. data required_v2_native_job_name: String = "required-v2-native" +// THE EMITTED CRATE'S RUSTFLAGS ARE THIS ROW, NOT THE RUNNER'S AMBIENT. Seed cargo for the +// required floor exports RUSTFLAGS=-D warnings for the SEED crate; inheriting that into the +// emitted compiler made the receipt true only on CI runners. The native lane's cargo invocation +// sets this value explicitly (and clears CARGO_ENCODED_RUSTFLAGS so an encoded ambient cannot +// override it). Authority cited by the host harness, not transcribed into a second string there. +data native_lane_emitted_compiler_rustflags: String = "-D warnings" + // THE UNIVERSE IS A PREFIX, NOT A ROSTER. The population is DERIVED from the tree's test // discovery by prefix membership — a hand-listed roster would silently drift from the tree it // claims to cover. The prefix is the same `v2.test.` the required floor gates on @@ -453,6 +460,23 @@ type NativeRouteOldRouteControl // reclassification's join key, carried so admission can re-ask the join, not trust it); the // OBSERVED population; the per-file front-end refusals the collecting fold gathered; and every // control. +// OPERABILITY THE JOB MUST PRINT, AND THE RECEIPT MUST CARRY. Identities already on the +// receipt (emitted_closure_identity, executable_identity) are the order-insensitive closure +// digest and the emitted binary digest. The rest is the cargo invocation's own facts plus the +// HostBudget standing: the lane runs under the runner slot's cgroup (gunbc.ci_floor_measurement +// gunbc_ci_runner_cgroup_memory_cap) and the receipt names the live reading against that budget. +type NativeRouteOperability { + cargo_command: String + effective_rustflags: String + rustc_identity: String + cargo_exit_status: Int + warning_count: Int + route_wall_ms: Int + peak_rss_bytes: Optional + admitted_budget_bytes: Optional + budget_source: String +} + type NativeRouteReceipt { job: String tested_tree: String @@ -468,6 +492,7 @@ type NativeRouteReceipt { true_control: Optional malformed_control: NativeRouteMalformedControl old_route_control: NativeRouteOldRouteControl + operability: NativeRouteOperability } // ADMISSION IS THE CONTRACT, ONE AUTHORITY, ONE CLAUSE PER PREDICATE. The job greens only when diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index fea864cf13f..fd97fa3eb6a 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -216,36 +216,29 @@ data heal_generated_artifacts_job_id: String = "heal-generated-artifacts" // admission comparing against a name nothing produces, refusing every candidate forever. data witness_floor_workflow_name: String = "witnesses" -// CANCELLATION IS A PER-EVENT FACT, AND THE GROUP CARRIES HALF OF IT. Operator ruling 2026-08-17: -// every push to main must finish; only pull-request runs may be superseded. The predecessor -// collapsed both axes into one group keyed on `github.ref` with an unconditional `true`, so -// consecutive pushes to main landed in the single group `witness-floor-refs/heads/main` and each -// new push killed the previous commit's run -- main's floor coverage was decided by push arrival, -// the empty-observation narrow section 5 forbids: a cancelled run reports no verdict, and no -// verdict was being read as nothing to report. -// -// Setting `cancel_in_progress` false for main is NOT sufficient on its own. GitHub cancels PENDING -// runs in a group regardless of that flag -- it only protects a run that has already started -- so -// a main run queued behind a busy fleet could still be superseded before it executed. The group -// must therefore be unique per main run, which `github.run_id` gives, and the flag must -// additionally be false there so the two mechanisms agree rather than one relying on the other. -// -// The cost is that N concurrent main runs may execute where one used to. That is the intended -// trade -- coverage of every landed commit, at fleet occupancy -- and the reason the dep-info race -// under `target/` is worth identifying before this is widened further. -// -// AND QUEUEING IS NOT THE THIRD ARM IT LOOKS LIKE. Sharing one main group with cancellation off -// makes GitHub QUEUE same-group runs rather than drop them, which reads as coverage without -// concurrency cost -- but the floor's merge-critical wall runs hours against a main push gap of -// minutes (run 31672107941, the last main run to complete before the 2026-08-13 measurement), so a -// queued policy accumulates an unbounded backlog and delivers each verdict long after the commit it -// judges. A unique key runs them concurrently instead, which is the only arm that yields a verdict -// per main commit at this arrival rate. This paragraph is carried here because it was the only -// record of that measurement, and it lived in `gunbc.ci_workflow_expressions` -- a SECOND -// concurrency authority left behind when the floor cut deleted the workflow it fed. That module's -// note asserted main was grouped by `github.sha` and "never run_id", which is not what CI runs; -// its rows are deleted with this one standing as the single authority. -data witness_floor_concurrency_group: String = "witness-floor-${{ github.event.pull_request.number || github.run_id }}" +// THE GROUP IS PER REF ON MAIN, PER PULL REQUEST ELSEWHERE, AND RUNNING SUPERSESSION STAYS OFF. +// +// Pull requests still key on `github.event.pull_request.number` so synchronize events share a +// group. Pushes, merge_group, and workflow_dispatch fall through to `github.ref` -- for main that +// is one group `witness-floor-refs/heads/main`, not a unique `run_id` per push. +// +// WHY PER-REF ON MAIN, AND WHY AN IN-FLIGHT MAIN RUN IS ALLOWED TO FINISH. The 2026-08-17 unique +// `run_id` key made every main commit start its own required run. That was the right occupancy +// trade when the required wall was the floor lane. The required-v2-native job is a different +// envelope: its producer is `gunbc.witness_v2_native_route` (the native-route census on the +// required-v2-native job), and two concurrent main pushes would occupy the shared arm64 runners +// for that whole envelope at once. Per-ref grouping serializes main. Running supersession stays +// `NeverSupersedeRunning` because `fleet_compile_cancellation_standing` is still +// `FleetCancellationNotSafe` -- a GitHub cancel SIGKILLs cargo and permanently leaks host jobserver +// permits (the 2026-08-30 srv1 measurement that retired the event-expression). Pending +// replacement (`ReplaceExistingPending`) is the other axis: GitHub drops a not-yet-started +// same-group run when a newer one arrives, so the backlog is one in-flight plus at most one +// queued successor, not an unbounded queue of 6h jobs and not N concurrent native jobs. +// +// WHAT IS GIVEN UP: a main commit whose run is still PENDING when the next push arrives never +// starts. The in-flight commit still receives a verdict. That is the occupancy decision, not +// a silent widen of cancel-in-progress. +data witness_floor_concurrency_group: String = "witness-floor-${{ github.event.pull_request.number || github.ref }}" // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // @@ -1684,12 +1677,10 @@ fn witness_floor_job() -> Job { // the v2 closure emission, one cargo build of the emitted compiler crate, the universe // derivation fold (the floor's own discovery producer over the full module inventory), and the // native run itself — one process over the derived v2.test.* universe plus the three controls. -// Measured 2026-09-10 on the 4-core cloud VM, after the lexer accumulation fix -// (v2.compiler.tokenize lexeme_grow): the native run dominates the envelope — ~70 minutes for -// the whole-corpus context fold plus the 3467-row eval, observed by the -// gunbc.witness_v2_native_route census — beside ~10 minutes of emission and ~10 minutes of -// cargo build. The envelope is 180 minutes, the floor lane's own value, so the merge queue's -// check_response_timeout (witness_floor_lane_timeout plus margin) still bounds it. +// THE ENVELOPE IS AN EXECUTION BOUND, NEVER A TARGET. The producer is +// gunbc.witness_v2_native_route (the required-v2-native census on the live job). Six hours is +// the declared envelope, ratcheted down later when that producer says the wall has dropped. +// The merge queue's check_response_timeout reads required_ci_slowest_lane_timeout plus margin. // // WHAT RED DISCRIMINATES. The admission authority is gunbc.witness_v2_native_route: the observed // population must join the derived universe exactly (no duplicates, no foreign rows, no @@ -1705,7 +1696,7 @@ fn witness_floor_job() -> Job { // control and the floor cannot share a job. The lane also builds a second crate (the emitted // compiler), a capability envelope the existing lanes do not declare. A job is the smallest // boundary that keeps both facts honest. -data v2_native_lane_timeout: Minute = minute(count: 180) +data v2_native_lane_timeout: Minute = minute(count: 360) fn v2_native_lane_build_script() -> String { repo_self_build_command(bins: witness_floor_required_bins) @@ -1775,12 +1766,11 @@ fn v2_native_lane_job() -> Job { } } -// THE LANE'S EXECUTION ENVELOPE, NAMED BECAUSE A SECOND CONSUMER NOW READS IT. The value did not -// change; it stopped being a literal. gunbc.repo_ruleset derives the merge queue's -// check_response_timeout from this row, because a queue whose response timeout is BELOW the -// repository's own execution envelope manufactures queue failures out of runs that were going to -// pass -- it dequeues green work for being slow. Two numbers that must not drift apart are one -// row, read twice (DESIGN section 3). +// THE FLOOR LANE'S EXECUTION ENVELOPE. The merge queue no longer reads this row alone: the +// required aggregate's slowest job is required_ci_slowest_lane_timeout (this row and +// v2_native_lane_timeout), and gunbc.repo_ruleset derives check_response_timeout from that +// maximum plus margin. Two numbers that must not drift apart stay one fold, read twice +// (DESIGN section 3). // // IT IS A Minute AND NOT AN Int, because the fact is a duration and std.measure already owns that // carrier. The Actions job field is an Int, so the projection unwraps it at the boundary -- which @@ -1789,6 +1779,14 @@ fn v2_native_lane_job() -> Job { // in its type. data witness_floor_lane_timeout: Minute = minute(count: 180) +fn required_ci_slowest_lane_timeout() -> Minute { + if minute_count(v2_native_lane_timeout) >= minute_count(witness_floor_lane_timeout) { + v2_native_lane_timeout + } else { + witness_floor_lane_timeout + } +} + // MERGE_GROUP IS ENROLLED SO THE REQUIRED CHECK CAN RUN ON THE COMPOSED COMMIT, NOT ONLY ON EACH // BRANCH HEAD. Without this trigger a merge queue would create its merge-group commit and this // workflow would never schedule for it, leaving the required check pending forever -- the diff --git a/dag/test/claim/repo_ruleset_witness_test.dag b/dag/test/claim/repo_ruleset_witness_test.dag index bc6370dc1ac..8306c54b37d 100644 --- a/dag/test/claim/repo_ruleset_witness_test.dag +++ b/dag/test/claim/repo_ruleset_witness_test.dag @@ -2,7 +2,7 @@ module test.claim.repo_ruleset_witness_test import std.types { Bool, String, Int, NonEmptyStr } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, witness_floor_lane_timeout } +import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, witness_floor_lane_timeout, v2_native_lane_timeout, required_ci_slowest_lane_timeout } import std.measure { minute, minute_count, merge_queue_entry_count, merge_queue_entry_count_value } import extdeps.github.actions { github_actions_app_integration_id } import gunbc.repo_ruleset { @@ -350,6 +350,11 @@ test fn w_the_queue_timeout_is_never_below_the_floors_execution_envelope() -> Bo minute_count(gunbc_merge_queue_proposal.check_response_timeout) > minute_count(witness_floor_lane_timeout) } +test fn w_the_queue_timeout_is_never_below_the_native_lanes_execution_envelope() -> Bool { + minute_count(gunbc_merge_queue_proposal.check_response_timeout) > minute_count(v2_native_lane_timeout) + && minute_count(required_ci_slowest_lane_timeout()) == minute_count(v2_native_lane_timeout) +} + // A LIVE QUEUE NOBODY SIGNED OFF IS REPORTED. This is the web-form change the module exists to stop // being invisible, and it must name WHICH guarantee the live queue provides -- an undeclared // HEADGREEN queue and an undeclared ALLGREEN one are different facts. diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 5b509477e8d..892c1b240c2 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -186,14 +186,14 @@ test fn w_RED_a_replacement_created_after_the_cancel_is_never_superseded() -> Bo // AVAILABILITY DOMINATES THE OBSERVATION, WHICH IS A STRICTLY STRONGER STATEMENT THAN ABSENCE. // // The two main-branch specimens in the measured window are cancelled push runs. A push cannot be -// superseded, because the emitted `concurrency.group` is -// `witness-floor-${{ github.event.pull_request.number || github.run_id }}` and a push carries no -// pull_request number, so it falls back to `run_id` and every push run is ALONE IN ITS GROUP. The -// reason is the group expression, NOT `cancel-in-progress`, which witnesses.yml sets to an -// UNCONDITIONAL `false` for every event -- so those runs are not merely unexplained, they are -// structurally incapable of the benign explanation. This -// asserts that ordering by handing a push run the MOST benign-looking evidence available (a -// positive-lag replacement) and requiring it to still classify external. +// superseded under the fixture policy, because that policy's running axis is +// `SupersedeRunningForEvent { event: pull_request }` and a push is not that event. The live +// workflow additionally keeps `NeverSupersedeRunning` (jobserver permit leak). The group is +// `witness_floor_concurrency_group` (`github.event.pull_request.number || github.ref`), so two +// main pushes SHARE a group; pending replacement can drop a not-yet-started successor, but an +// in-flight main run is not cancelled. This asserts that ordering by handing a push run the MOST +// benign-looking evidence available (a positive-lag replacement) and requiring it to still +// classify external. test fn w_RED_a_push_cancel_is_external_even_with_a_prior_replacement() -> Bool { let push = cancelled_run(event: "push") let d = classify_run_under(spec: superseding_policy(), pending: ReplaceExistingPending, record: push, replacement: ReplacementObserved { lag_seconds: 27 }) diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 37df9718e0f..aee7225ff6f 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -1,9 +1,10 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } +import std.measure { minute_count } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, witness_floor_concurrency_group, v2_native_lane_timeout } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -469,3 +470,16 @@ test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subjec WitnessFloorGenerationRefused { reason: _ } => false } } + +test fn w_main_concurrency_group_is_per_ref_not_per_run_id() -> Bool { + string_contains(s: witness_floor_concurrency_group, pattern: "github.ref") + && !string_contains(s: witness_floor_concurrency_group, pattern: "github.run_id") + && minute_count(v2_native_lane_timeout) == 360 + && match expected_witness_floor_yml() { + WitnessFloorGenerated { content: yml } => + string_contains(s: yml, pattern: "timeout-minutes: 360") + && string_contains(s: yml, pattern: "github.event.pull_request.number || github.ref") + && !string_contains(s: yml, pattern: "github.event.pull_request.number || github.run_id") + WitnessFloorGenerationRefused { reason: _ } => false + } +} 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 ed2a0dc7de9..abaf32b194f 100644 --- a/src/v1/stage0/src/cli_run/native_lane_runner.rs +++ b/src/v1/stage0/src/cli_run/native_lane_runner.rs @@ -84,11 +84,19 @@ struct NativeFileRefusalObserved { /// The emitted compiler, prepared: where the binary is, what its bytes are, and the identity of /// the closure it was emitted from. +/// Seed mirror of `gunbc.witness_v2_native_route` `native_lane_emitted_compiler_rustflags`. +const NATIVE_LANE_EMITTED_COMPILER_RUSTFLAGS: &str = "-D warnings"; + struct EmittedPreparation { binary_path: PathBuf, binary_identity: String, closure_identity: String, seed_identity: String, + cargo_command: String, + effective_rustflags: String, + rustc_identity: String, + cargo_exit_status: i32, + warning_count: u32, } fn sha256_file(path: &Path) -> Result { @@ -303,17 +311,28 @@ fn prepare_emitted_compiler(source_roots: &[String]) -> Result *status, + _ => -1, + }; + if !super::emitted_closure_compile_host::cargo_verdict_compiled(&cargo_run.verdict) { return Err(format!( "V2-NATIVE REFUSAL cause=EmittedCompilerBuildFailed — {}", - super::emitted_closure_compile_host::cargo_verdict_summary(&verdict) + super::emitted_closure_compile_host::cargo_verdict_summary(&cargo_run.verdict) )); } + let rustc_identity = rustc_verbose_identity(); + eprintln!( + "required-ci: v2-native cargo_command={} effective_RUSTFLAGS={} rustc_identity={} \ + cargo_exit_status={cargo_exit_status} warning_count={}", + cargo_run.command, cargo_run.rustflags, rustc_identity, cargo_run.warning_count + ); let binary_path = workspace.join("target").join("release").join( super::emitted_closure_compile_host::probe_package_name(NATIVE_COMPILE_ENTRY), ); @@ -337,9 +356,26 @@ fn prepare_emitted_compiler(source_roots: &[String]) -> Result String { + let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); + match Command::new(&rustc).arg("-vV").output() { + Ok(output) => String::from_utf8_lossy(&output.stdout) + .lines() + .next() + .unwrap_or("rustc -vV produced no lines") + .to_string(), + Err(e) => format!("rustc -vV failed: {e}"), + } +} + /// THE OLD-ROUTE WITHDRAWAL, AS A GUARD SO A REFUSAL PATH CANNOT SKIP THE RESTORE. The seed's /// `gunbc` binary — the old compiler/interpreter route's CLI — is renamed out of the way before /// the first native spawn and restored when the guard drops, on every exit path. The receipt @@ -698,6 +734,63 @@ fn optional_verdict_value( } } +fn optional_int(ctx: &v1_interpreter::InterpContext, value: Option) -> Value { + match value { + Some(n) => Value::Variant { + type_name: ctx.sym("Optional"), + variant_name: ctx.sym("Present"), + fields: Rc::new(vec![(ctx.sym("value"), Value::Int(n))]), + }, + None => Value::Variant { + type_name: ctx.sym("Optional"), + variant_name: ctx.sym("Absent"), + fields: Rc::new(vec![]), + }, + } +} + +fn operability_value( + ctx: &v1_interpreter::InterpContext, + preparation: &EmittedPreparation, + route_wall_ms: i64, + peak_rss_bytes: Option, + admitted_budget_bytes: Option, + budget_source: &str, +) -> Value { + Value::Record { + type_name: ctx.sym("NativeRouteOperability"), + fields: Rc::new(vec![ + ( + ctx.sym("cargo_command"), + str_value(&preparation.cargo_command), + ), + ( + ctx.sym("effective_rustflags"), + str_value(&preparation.effective_rustflags), + ), + ( + ctx.sym("rustc_identity"), + str_value(&preparation.rustc_identity), + ), + ( + ctx.sym("cargo_exit_status"), + Value::Int(i64::from(preparation.cargo_exit_status)), + ), + ( + ctx.sym("warning_count"), + Value::Int(i64::from(preparation.warning_count)), + ), + (ctx.sym("route_wall_ms"), Value::Int(route_wall_ms)), + (ctx.sym("peak_rss_bytes"), optional_int(ctx, peak_rss_bytes)), + ( + ctx.sym("admitted_budget_bytes"), + optional_int(ctx, admitted_budget_bytes), + ), + (ctx.sym("budget_source"), str_value(budget_source)), + ]), + } +} + #[allow(clippy::too_many_arguments)] fn receipt_value( ctx: &v1_interpreter::InterpContext, @@ -711,6 +804,10 @@ fn receipt_value( true_control: Option<&NativeVerdictObserved>, malformed_control: Value, withdrawn_executable: &str, + route_wall_ms: i64, + peak_rss_bytes: Option, + admitted_budget_bytes: Option, + budget_source: &str, ) -> Value { let universe_values: Vec = universe .iter() @@ -807,6 +904,17 @@ fn receipt_value( )]), }, ), + ( + ctx.sym("operability"), + operability_value( + ctx, + preparation, + route_wall_ms, + peak_rss_bytes, + admitted_budget_bytes, + budget_source, + ), + ), ]), } } @@ -818,6 +926,7 @@ fn receipt_value( /// `native_route_admission_summary` of that same value, so the terminal line and the admission /// fold cannot disagree about what was decided. pub fn run_required_v2_native(source_roots: &[String]) -> Result<(), String> { + let lane_started = std::time::Instant::now(); let workspace = super::process_workspace_root(); let tested_tree = std::env::var("GITHUB_SHA").unwrap_or_else(|_| "local".to_string()); @@ -959,6 +1068,30 @@ pub fn run_required_v2_native(source_roots: &[String]) -> Result<(), String> { ]), } }; + let route_wall_ms = i64::try_from(lane_started.elapsed().as_millis()).unwrap_or(i64::MAX); + let (_, cgroup_peak) = super::p1_cohort::p1_cohort_cgroup_memory(); + let peak_rss_bytes = cgroup_peak + .or_else(super::peak_rss_vhwm_bytes) + .and_then(|b| i64::try_from(b).ok()); + let budget_resolution = crate::memory_governor::read_host_budget_resolution(); + let admitted_budget_bytes = budget_resolution + .bytes() + .and_then(|b| i64::try_from(b).ok()); + let budget_source = budget_resolution.label(); + eprintln!( + "required-ci: v2-native receipt fields closure_digest={} emitted_binary_digest={} \ + cargo_command={} effective_RUSTFLAGS={} rustc_identity={} cargo_exit_status={} \ + warning_count={} route_wall_ms={route_wall_ms} peak_rss_bytes={peak_rss_bytes:?} \ + admitted_budget_bytes={admitted_budget_bytes:?} budget_source={budget_source} \ + (budget authority gunbc.ci_floor_measurement.gunbc_ci_runner_cgroup_memory_cap)", + preparation.closure_identity, + preparation.binary_identity, + preparation.cargo_command, + preparation.effective_rustflags, + preparation.rustc_identity, + preparation.cargo_exit_status, + preparation.warning_count + ); let receipt = receipt_value( &route_ctx, &tested_tree, @@ -971,6 +1104,10 @@ pub fn run_required_v2_native(source_roots: &[String]) -> Result<(), String> { true_control.as_ref(), malformed_value, &withdrawn_executable, + route_wall_ms, + peak_rss_bytes, + admitted_budget_bytes, + &budget_source, ); // 8. ADMISSION, BY THE AUTHORITY. The lane prints the authority's own summary either way. diff --git a/src/v1/stage0/src/emitted_closure_compile_host.rs b/src/v1/stage0/src/emitted_closure_compile_host.rs index 610d7d611d2..c204a3ea096 100644 --- a/src/v1/stage0/src/emitted_closure_compile_host.rs +++ b/src/v1/stage0/src/emitted_closure_compile_host.rs @@ -772,36 +772,84 @@ pub(crate) fn run_cargo( workspace: &Path, attribution_symbol: &str, ) -> CargoVerdict { + run_cargo_recorded(crate_dir, workspace, attribution_symbol, None).verdict +} + +pub(crate) struct CargoInvocationRecord { + pub verdict: CargoVerdict, + pub command: String, + pub rustflags: String, + pub warning_count: u32, +} + +/// Same cargo as `run_cargo`, with an optional explicit RUSTFLAGS value that replaces any ambient +/// flag-bearing variable. The native lane passes +/// `gunbc.witness_v2_native_route` `native_lane_emitted_compiler_rustflags`. +pub(crate) fn run_cargo_recorded( + crate_dir: &Path, + workspace: &Path, + attribution_symbol: &str, + rustflags: Option<&str>, +) -> CargoInvocationRecord { let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + let manifest = crate_dir.join("Cargo.toml"); + let command_text = format!( + "{cargo} build --release --manifest-path {}", + manifest.display() + ); + let rustflags_text = rustflags.unwrap_or("").to_string(); let mut command = std::process::Command::new(&cargo); command .arg("build") .arg("--release") .arg("--manifest-path") - .arg(crate_dir.join("Cargo.toml")) + .arg(&manifest) .env("CARGO_TARGET_DIR", workspace.join("target")) .current_dir(crate_dir); - match command.output() { - Err(e) => CargoVerdict::DidNotComplete { - detail: format!("spawning {cargo} failed: {e}"), - }, - Ok(output) => match output.status.code() { - None => CargoVerdict::DidNotComplete { - detail: format!("{cargo} terminated by signal without an exit status"), + if let Some(flags) = rustflags { + command.env_remove("CARGO_ENCODED_RUSTFLAGS"); + command.env("RUSTFLAGS", flags); + } + let (verdict, warning_count) = match command.output() { + Err(e) => ( + CargoVerdict::DidNotComplete { + detail: format!("spawning {cargo} failed: {e}"), }, + 0, + ), + Ok(output) => match output.status.code() { + None => ( + CargoVerdict::DidNotComplete { + detail: format!("{cargo} terminated by signal without an exit status"), + }, + 0, + ), Some(status) => { let stderr = String::from_utf8_lossy(&output.stderr); + let warning_count = stderr + .lines() + .filter(|line| line.trim_start().starts_with("warning:")) + .count() as u32; let tail: Vec<&str> = stderr.lines().rev().take(20).collect(); let (probe_line, probe_diagnostic) = attributed_diagnostic(&stderr, attribution_symbol); - CargoVerdict::Completed { - status, - stderr_tail: tail.into_iter().rev().collect::>().join("\n"), - probe_line, - probe_diagnostic, - } + ( + CargoVerdict::Completed { + status, + stderr_tail: tail.into_iter().rev().collect::>().join("\n"), + probe_line, + probe_diagnostic, + }, + warning_count, + ) } }, + }; + CargoInvocationRecord { + verdict, + command: command_text, + rustflags: rustflags_text, + warning_count, } } diff --git a/src/v2/test/v2_native_route_test.dag b/src/v2/test/v2_native_route_test.dag index d4b5cbe7bdd..6487371aa5c 100644 --- a/src/v2/test/v2_native_route_test.dag +++ b/src/v2/test/v2_native_route_test.dag @@ -19,9 +19,9 @@ import gunbc.witness_v2_native_route { NativeRouteCensus, native_route_census, NativeRouteMalformedControl, MalformedSpecimenRefused, MalformedSpecimenAccepted, NativeRouteOldRouteControl, OldRouteWithdrawn, OldRouteFalsifierArmed, OldRouteControlAbsent, - NativeRouteReceipt, + NativeRouteReceipt, NativeRouteOperability, NativeRouteAdmission, NativeRouteAdmitted, NativeRouteRefused, - required_v2_native_job_name, + required_v2_native_job_name, native_lane_emitted_compiler_rustflags, native_route_admission, native_route_admitted, native_route_admission_refused_with, native_route_admission_summary } @@ -39,6 +39,20 @@ import v2.std.logic { Bool } import v2.std.node { Symbol } import v2.std.integer { Int } +fn synthetic_operability() -> NativeRouteOperability { + NativeRouteOperability { + cargo_command: "cargo build --release --manifest-path probe/Cargo.toml", + effective_rustflags: native_lane_emitted_compiler_rustflags, + rustc_identity: "rustc test", + cargo_exit_status: 0, + warning_count: 0, + route_wall_ms: 1, + peak_rss_bytes: Present { value: 1 }, + admitted_budget_bytes: Present { value: 1 }, + budget_source: "test" + } +} + // ── synthetic receipt constructors ───────────────────────────────────────────────────────── // // The admission fold reads the REAL floor rosters (floor_expected_red_roster, @@ -107,7 +121,8 @@ fn receipt_over( path: "target/v2-native-lane/malformed-control-root/poison.dag", reason: ^tokenize_lex_e1_unterminated_string }, - old_route_control: OldRouteWithdrawn { withdrawn_executable: "/seed/gunbc" } + old_route_control: OldRouteWithdrawn { withdrawn_executable: "/seed/gunbc" }, + operability: synthetic_operability() } } @@ -430,7 +445,8 @@ test fn the_wrong_job_name_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control + old_route_control: green_receipt().old_route_control, + operability: green_receipt().operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -453,7 +469,8 @@ test fn an_unrecorded_tested_tree_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control + old_route_control: green_receipt().old_route_control, + operability: green_receipt().operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -476,7 +493,8 @@ test fn an_unrecorded_preparation_seed_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control + old_route_control: green_receipt().old_route_control, + operability: green_receipt().operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -499,7 +517,8 @@ test fn an_unrecorded_emitted_closure_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control + old_route_control: green_receipt().old_route_control, + operability: green_receipt().operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -523,7 +542,8 @@ test fn an_unrecorded_route_identity_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } let no_identity = NativeRouteReceipt { job: base.job, @@ -539,7 +559,8 @@ test fn an_unrecorded_route_identity_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } native_route_admission_refused_with( admission: native_route_admission(receipt: no_path), @@ -906,7 +927,8 @@ test fn a_missing_false_control_is_refused() -> Bool { false_control: Absent, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -930,7 +952,8 @@ test fn a_passed_false_control_is_refused() -> Bool { false_control: Present { value: NativeTestPassed }, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -954,7 +977,8 @@ test fn a_missing_true_control_is_refused() -> Bool { false_control: base.false_control, true_control: Absent, malformed_control: base.malformed_control, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -978,7 +1002,8 @@ test fn a_falsified_true_control_is_refused() -> Bool { false_control: base.false_control, true_control: Present { value: NativeTestReturnedFalse }, malformed_control: base.malformed_control, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1002,7 +1027,8 @@ test fn an_accepted_malformed_specimen_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: MalformedSpecimenAccepted, - old_route_control: base.old_route_control + old_route_control: base.old_route_control, + operability: base.operability } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1026,7 +1052,8 @@ test fn an_absent_old_route_control_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: OldRouteControlAbsent + old_route_control: OldRouteControlAbsent, + operability: synthetic_operability() } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1051,7 +1078,8 @@ test fn one_red_run_names_every_violated_clause() -> Bool { false_control: Absent, true_control: Absent, malformed_control: MalformedSpecimenAccepted, - old_route_control: OldRouteControlAbsent + old_route_control: OldRouteControlAbsent, + operability: synthetic_operability() }) native_route_admission_refused_with(admission: admission, cause_name: "job_name_mismatch") && native_route_admission_refused_with(admission: admission, cause_name: "tested_tree_unrecorded") @@ -1082,6 +1110,11 @@ test fn the_admission_summary_names_every_violated_clause() -> Bool { false_control: Absent, true_control: Absent, malformed_control: MalformedSpecimenAccepted, - old_route_control: OldRouteControlAbsent + old_route_control: OldRouteControlAbsent, + operability: synthetic_operability() })) == "refused: job_name_mismatch, tested_tree_unrecorded, preparation_seed_unrecorded, emitted_closure_unrecorded, route_identity_unrecorded, universe_underived, positive_population_empty, required_native_pass_regressed, false_control_not_observed, true_control_not_observed, malformed_control_accepted_by_route, old_route_control_never_installed" } + +test fn emitted_compiler_rustflags_are_explicit_deny_warnings() -> Bool { + native_lane_emitted_compiler_rustflags == "-D warnings" +} From fe8e5be96a637f20ef6af5031d4ae3dcdfbd553c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 10 Sep 2026 23:21:53 +0000 Subject: [PATCH 02/27] Scope P0a to native-job supersession and a 360-minute envelope. Workflow-level uniqueness still keeps build and floor verdicts. The required-v2-native job now groups per ref and cancels the previous native job for that main branch or the same PR. Timeout stays a temporary truthful envelope; warnings, HostBudget, and receipt fields wait for P0b. Co-authored-by: Cursor --- .github/workflows/witnesses.yml | 5 +- .../fleet_desired_admission_workflow.dag | 9 +- dag/gunbc/floor/floor_attempt_standing.dag | 7 +- dag/gunbc/witness/v2_native_route.dag | 25 --- dag/gunbc/witness/witness_floor_workflow.dag | 78 +++++++--- .../claim/run_disposition_witness_test.dag | 16 +- ...or_workflow_consolidation_witness_test.dag | 12 +- .../stage0/src/cli_run/native_lane_runner.rs | 143 +----------------- .../src/emitted_closure_compile_host.rs | 76 ++-------- src/v2/test/v2_native_route_test.dag | 67 +++----- 10 files changed, 112 insertions(+), 326 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 03957839178..20f4b19433c 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -17,7 +17,7 @@ on: permissions: contents: read concurrency: - group: witness-floor-${{ github.event.pull_request.number || github.ref }} + group: witness-floor-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: false env: CARGO_TERM_COLOR: always @@ -406,6 +406,9 @@ jobs: required-v2-native: runs-on: [self-hosted, linux, arm64] timeout-minutes: 360 + concurrency: + group: required-v2-native-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true steps: - name: Record runner filesystem at job start run: | diff --git a/dag/gunbc/fleet/fleet_desired_admission_workflow.dag b/dag/gunbc/fleet/fleet_desired_admission_workflow.dag index 82077cbb53a..6241cfe617b 100644 --- a/dag/gunbc/fleet/fleet_desired_admission_workflow.dag +++ b/dag/gunbc/fleet/fleet_desired_admission_workflow.dag @@ -325,12 +325,9 @@ fn fleet_desired_admission_job() -> Job { // WHAT ORDERING DOES NOT COVER: THE DESIRED REF CAN MOVE BACKWARD, AND THIS IS ORDINARY RATHER // THAN AN EDGE CASE. An earlier revision of this annotation said reaching it "requires deliberately // re-running an old floor run". THAT UNDERSTATED IT AND WAS WRONG. The floor's own concurrency -// group is `witness-floor-${{ github.event.pull_request.number || github.ref }}`, and on a -// default-branch push there is no pull-request number, so the group is the branch ref -- main -// runs SERIALIZE. An in-flight main run is allowed to finish (`NeverSupersedeRunning`); a -// not-yet-started same-group run is replaced. Two merges therefore no longer start two native -// jobs at once. The residual race is a finishing older in-flight admission versus a later -// successor that started after it: +// group is `witness-floor-${{ github.event.pull_request.number || github.run_id }}`, and on a +// default-branch push there is no pull-request number, so the group is unique per run -- the floor +// imposes NO ordering between main pushes. Two merges therefore race by construction: // // floor(C1) starts; floor(C2) starts // floor(C2) finishes first -> admission advances D -> C2 diff --git a/dag/gunbc/floor/floor_attempt_standing.dag b/dag/gunbc/floor/floor_attempt_standing.dag index 45a978985bf..5346526edca 100644 --- a/dag/gunbc/floor/floor_attempt_standing.dag +++ b/dag/gunbc/floor/floor_attempt_standing.dag @@ -549,10 +549,9 @@ fn floor_rendered_verdict_name(verdict: FloorRenderedVerdict) -> String { // ACQUISITION rather than of this composition: nothing in the required run reads the pull // request's current head, so no two observations can disagree. The census behind that statement: // no step in any emitted workflow shells to `gh api`, `gh run` or `gh pr` or curls the API; the -// only run-metadata expressions in the emitted workflow are `github.ref` inside the witnesses -// concurrency key (a key string, not an observation), `github.run_id` on the aggregate job's -// env, and the usual checkout `github.sha` interpolations; and `witnesses.yml` grants -// `contents: read` with no token in any job's env, so no such step could authenticate if it existed. +// only run-metadata expression anywhere is `github.run_id` inside the witnesses concurrency key, +// which is a key string and not an observation; and `witnesses.yml` grants `contents: read` with +// no token in any job's env, so no such step could authenticate if it existed. // // THE SHAPE IS NOT NET-NEW, ONLY ITS ACQUISITION HERE IS. `extdeps.github.pulls` declares // `github.Pulls.Get` over a rest transport returning `PullRequest`, whose `head` carries `sha`, diff --git a/dag/gunbc/witness/v2_native_route.dag b/dag/gunbc/witness/v2_native_route.dag index 4b2ddf023a2..b0336d076e9 100644 --- a/dag/gunbc/witness/v2_native_route.dag +++ b/dag/gunbc/witness/v2_native_route.dag @@ -47,13 +47,6 @@ import v2.workflow.floor_route_gap { floor_route_gap_roster } // all cite this declaration; no second spelling of the job name anywhere. data required_v2_native_job_name: String = "required-v2-native" -// THE EMITTED CRATE'S RUSTFLAGS ARE THIS ROW, NOT THE RUNNER'S AMBIENT. Seed cargo for the -// required floor exports RUSTFLAGS=-D warnings for the SEED crate; inheriting that into the -// emitted compiler made the receipt true only on CI runners. The native lane's cargo invocation -// sets this value explicitly (and clears CARGO_ENCODED_RUSTFLAGS so an encoded ambient cannot -// override it). Authority cited by the host harness, not transcribed into a second string there. -data native_lane_emitted_compiler_rustflags: String = "-D warnings" - // THE UNIVERSE IS A PREFIX, NOT A ROSTER. The population is DERIVED from the tree's test // discovery by prefix membership — a hand-listed roster would silently drift from the tree it // claims to cover. The prefix is the same `v2.test.` the required floor gates on @@ -460,23 +453,6 @@ type NativeRouteOldRouteControl // reclassification's join key, carried so admission can re-ask the join, not trust it); the // OBSERVED population; the per-file front-end refusals the collecting fold gathered; and every // control. -// OPERABILITY THE JOB MUST PRINT, AND THE RECEIPT MUST CARRY. Identities already on the -// receipt (emitted_closure_identity, executable_identity) are the order-insensitive closure -// digest and the emitted binary digest. The rest is the cargo invocation's own facts plus the -// HostBudget standing: the lane runs under the runner slot's cgroup (gunbc.ci_floor_measurement -// gunbc_ci_runner_cgroup_memory_cap) and the receipt names the live reading against that budget. -type NativeRouteOperability { - cargo_command: String - effective_rustflags: String - rustc_identity: String - cargo_exit_status: Int - warning_count: Int - route_wall_ms: Int - peak_rss_bytes: Optional - admitted_budget_bytes: Optional - budget_source: String -} - type NativeRouteReceipt { job: String tested_tree: String @@ -492,7 +468,6 @@ type NativeRouteReceipt { true_control: Optional malformed_control: NativeRouteMalformedControl old_route_control: NativeRouteOldRouteControl - operability: NativeRouteOperability } // ADMISSION IS THE CONTRACT, ONE AUTHORITY, ONE CLAUSE PER PREDICATE. The job greens only when diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index fd97fa3eb6a..bb71f7aab22 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -216,29 +216,40 @@ data heal_generated_artifacts_job_id: String = "heal-generated-artifacts" // admission comparing against a name nothing produces, refusing every candidate forever. data witness_floor_workflow_name: String = "witnesses" -// THE GROUP IS PER REF ON MAIN, PER PULL REQUEST ELSEWHERE, AND RUNNING SUPERSESSION STAYS OFF. -// -// Pull requests still key on `github.event.pull_request.number` so synchronize events share a -// group. Pushes, merge_group, and workflow_dispatch fall through to `github.ref` -- for main that -// is one group `witness-floor-refs/heads/main`, not a unique `run_id` per push. -// -// WHY PER-REF ON MAIN, AND WHY AN IN-FLIGHT MAIN RUN IS ALLOWED TO FINISH. The 2026-08-17 unique -// `run_id` key made every main commit start its own required run. That was the right occupancy -// trade when the required wall was the floor lane. The required-v2-native job is a different -// envelope: its producer is `gunbc.witness_v2_native_route` (the native-route census on the -// required-v2-native job), and two concurrent main pushes would occupy the shared arm64 runners -// for that whole envelope at once. Per-ref grouping serializes main. Running supersession stays -// `NeverSupersedeRunning` because `fleet_compile_cancellation_standing` is still -// `FleetCancellationNotSafe` -- a GitHub cancel SIGKILLs cargo and permanently leaks host jobserver -// permits (the 2026-08-30 srv1 measurement that retired the event-expression). Pending -// replacement (`ReplaceExistingPending`) is the other axis: GitHub drops a not-yet-started -// same-group run when a newer one arrives, so the backlog is one in-flight plus at most one -// queued successor, not an unbounded queue of 6h jobs and not N concurrent native jobs. -// -// WHAT IS GIVEN UP: a main commit whose run is still PENDING when the next push arrives never -// starts. The in-flight commit still receives a verdict. That is the occupancy decision, not -// a silent widen of cancel-in-progress. -data witness_floor_concurrency_group: String = "witness-floor-${{ github.event.pull_request.number || github.ref }}" +// CANCELLATION IS A PER-EVENT FACT, AND THE GROUP CARRIES HALF OF IT. Operator ruling 2026-08-17: +// every push to main must finish; only pull-request runs may be superseded. The predecessor +// collapsed both axes into one group keyed on `github.ref` with an unconditional `true`, so +// consecutive pushes to main landed in the single group `witness-floor-refs/heads/main` and each +// new push killed the previous commit's run -- main's floor coverage was decided by push arrival, +// the empty-observation narrow section 5 forbids: a cancelled run reports no verdict, and no +// verdict was being read as nothing to report. +// +// Setting `cancel_in_progress` false for main is NOT sufficient on its own. GitHub cancels PENDING +// runs in a group regardless of that flag -- it only protects a run that has already started -- so +// a main run queued behind a busy fleet could still be superseded before it executed. The group +// must therefore be unique per main run, which `github.run_id` gives, and the flag must +// additionally be false there so the two mechanisms agree rather than one relying on the other. +// +// The cost is that N concurrent main runs may execute where one used to. That is the intended +// trade -- coverage of every landed commit, at fleet occupancy -- and the reason the dep-info race +// under `target/` is worth identifying before this is widened further. +// +// AND QUEUEING IS NOT THE THIRD ARM IT LOOKS LIKE. Sharing one main group with cancellation off +// makes GitHub QUEUE same-group runs rather than drop them, which reads as coverage without +// concurrency cost -- but the floor's merge-critical wall runs hours against a main push gap of +// minutes (run 31672107941, the last main run to complete before the 2026-08-13 measurement), so a +// queued policy accumulates an unbounded backlog and delivers each verdict long after the commit it +// judges. A unique key runs them concurrently instead, which is the only arm that yields a verdict +// per main commit at this arrival rate. This paragraph is carried here because it was the only +// record of that measurement, and it lived in `gunbc.ci_workflow_expressions` -- a SECOND +// concurrency authority left behind when the floor cut deleted the workflow it fed. That module's +// note asserted main was grouped by `github.sha` and "never run_id", which is not what CI runs; +// its rows are deleted with this one standing as the single authority. +// +// THE NATIVE LANE IS NOT THIS GROUP. Workflow-level uniqueness keeps build and floor verdicts +// for every run. The required-v2-native job carries its own per-ref group +// (v2_native_lane_concurrency_group) so a newer head cancels only that job. +data witness_floor_concurrency_group: String = "witness-floor-${{ github.event.pull_request.number || github.run_id }}" // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // @@ -1749,6 +1760,25 @@ fn v2_native_lane_steps() -> List { fn v2_native_lane_needs() -> List { [] } +// SUPERSESSION IS THIS JOB, NOT THE WORKFLOW. Workflow-level NeverSupersedeRunning keeps +// build and floor verdicts for every run (unique run_id group). The native job is the +// occupancy: it holds an arm64 runner for v2_native_lane_timeout, and a newer head of the +// SAME ref is the observation that matters. A newer main push cancels the previous main +// native job; a newer PR synchronize cancels the previous native job for that PR number +// only -- never another PR. cancel-in-progress is true here because leaving the old native +// job running is the fleet saturation (every PR push claiming a runner for the envelope); +// the jobserver-permit leak that retired workflow-level supersession still applies, and +// this row is the occupancy exception, not a silent reopen of that leak on build/floor. +data v2_native_lane_concurrency_group: String = "required-v2-native-${{ github.event.pull_request.number || github.ref }}" + +fn v2_native_lane_concurrency() -> ConcurrencySpec { + ConcurrencyMappingQueueNotMax { + group: v2_native_lane_concurrency_group, + cancel_in_progress: Present { value: CancelInProgressBool { value: true } }, + explicit_single: none + } +} + fn v2_native_lane_job() -> Job { Job { id: required_v2_native_job_name, @@ -1761,7 +1791,7 @@ fn v2_native_lane_job() -> Job { if_condition: none, timeout_minutes: minute_count(v2_native_lane_timeout), continue_on_error: none, - concurrency: none, + concurrency: Present { value: v2_native_lane_concurrency() }, permissions: none } } diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 892c1b240c2..5b509477e8d 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -186,14 +186,14 @@ test fn w_RED_a_replacement_created_after_the_cancel_is_never_superseded() -> Bo // AVAILABILITY DOMINATES THE OBSERVATION, WHICH IS A STRICTLY STRONGER STATEMENT THAN ABSENCE. // // The two main-branch specimens in the measured window are cancelled push runs. A push cannot be -// superseded under the fixture policy, because that policy's running axis is -// `SupersedeRunningForEvent { event: pull_request }` and a push is not that event. The live -// workflow additionally keeps `NeverSupersedeRunning` (jobserver permit leak). The group is -// `witness_floor_concurrency_group` (`github.event.pull_request.number || github.ref`), so two -// main pushes SHARE a group; pending replacement can drop a not-yet-started successor, but an -// in-flight main run is not cancelled. This asserts that ordering by handing a push run the MOST -// benign-looking evidence available (a positive-lag replacement) and requiring it to still -// classify external. +// superseded, because the emitted `concurrency.group` is +// `witness-floor-${{ github.event.pull_request.number || github.run_id }}` and a push carries no +// pull_request number, so it falls back to `run_id` and every push run is ALONE IN ITS GROUP. The +// reason is the group expression, NOT `cancel-in-progress`, which witnesses.yml sets to an +// UNCONDITIONAL `false` for every event -- so those runs are not merely unexplained, they are +// structurally incapable of the benign explanation. This +// asserts that ordering by handing a push run the MOST benign-looking evidence available (a +// positive-lag replacement) and requiring it to still classify external. test fn w_RED_a_push_cancel_is_external_even_with_a_prior_replacement() -> Bool { let push = cancelled_run(event: "push") let d = classify_run_under(spec: superseding_policy(), pending: ReplaceExistingPending, record: push, replacement: ReplacementObserved { lag_seconds: 27 }) diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index aee7225ff6f..c43a91b91cb 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -4,7 +4,7 @@ import std.types { Bool, List, String } import std.measure { minute_count } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, witness_floor_concurrency_group, v2_native_lane_timeout } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, v2_native_lane_concurrency_group, v2_native_lane_timeout } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -471,15 +471,15 @@ test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subjec } } -test fn w_main_concurrency_group_is_per_ref_not_per_run_id() -> Bool { - string_contains(s: witness_floor_concurrency_group, pattern: "github.ref") - && !string_contains(s: witness_floor_concurrency_group, pattern: "github.run_id") +test fn w_native_job_supersedes_per_ref_without_cancelling_the_workflow() -> Bool { + string_contains(s: v2_native_lane_concurrency_group, pattern: "github.event.pull_request.number || github.ref") && minute_count(v2_native_lane_timeout) == 360 && match expected_witness_floor_yml() { WitnessFloorGenerated { content: yml } => string_contains(s: yml, pattern: "timeout-minutes: 360") - && string_contains(s: yml, pattern: "github.event.pull_request.number || github.ref") - && !string_contains(s: yml, pattern: "github.event.pull_request.number || github.run_id") + && string_contains(s: yml, pattern: "required-v2-native-${{ github.event.pull_request.number || github.ref }}") + && string_contains(s: yml, pattern: "group: witness-floor-${{ github.event.pull_request.number || github.run_id }}") + && !string_contains(s: yml, pattern: "group: witness-floor-${{ github.event.pull_request.number || github.ref }}") WitnessFloorGenerationRefused { reason: _ } => false } } 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 abaf32b194f..ed2a0dc7de9 100644 --- a/src/v1/stage0/src/cli_run/native_lane_runner.rs +++ b/src/v1/stage0/src/cli_run/native_lane_runner.rs @@ -84,19 +84,11 @@ struct NativeFileRefusalObserved { /// The emitted compiler, prepared: where the binary is, what its bytes are, and the identity of /// the closure it was emitted from. -/// Seed mirror of `gunbc.witness_v2_native_route` `native_lane_emitted_compiler_rustflags`. -const NATIVE_LANE_EMITTED_COMPILER_RUSTFLAGS: &str = "-D warnings"; - struct EmittedPreparation { binary_path: PathBuf, binary_identity: String, closure_identity: String, seed_identity: String, - cargo_command: String, - effective_rustflags: String, - rustc_identity: String, - cargo_exit_status: i32, - warning_count: u32, } fn sha256_file(path: &Path) -> Result { @@ -311,28 +303,17 @@ fn prepare_emitted_compiler(source_roots: &[String]) -> Result *status, - _ => -1, - }; - if !super::emitted_closure_compile_host::cargo_verdict_compiled(&cargo_run.verdict) { + if !super::emitted_closure_compile_host::cargo_verdict_compiled(&verdict) { return Err(format!( "V2-NATIVE REFUSAL cause=EmittedCompilerBuildFailed — {}", - super::emitted_closure_compile_host::cargo_verdict_summary(&cargo_run.verdict) + super::emitted_closure_compile_host::cargo_verdict_summary(&verdict) )); } - let rustc_identity = rustc_verbose_identity(); - eprintln!( - "required-ci: v2-native cargo_command={} effective_RUSTFLAGS={} rustc_identity={} \ - cargo_exit_status={cargo_exit_status} warning_count={}", - cargo_run.command, cargo_run.rustflags, rustc_identity, cargo_run.warning_count - ); let binary_path = workspace.join("target").join("release").join( super::emitted_closure_compile_host::probe_package_name(NATIVE_COMPILE_ENTRY), ); @@ -356,26 +337,9 @@ fn prepare_emitted_compiler(source_roots: &[String]) -> Result String { - let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); - match Command::new(&rustc).arg("-vV").output() { - Ok(output) => String::from_utf8_lossy(&output.stdout) - .lines() - .next() - .unwrap_or("rustc -vV produced no lines") - .to_string(), - Err(e) => format!("rustc -vV failed: {e}"), - } -} - /// THE OLD-ROUTE WITHDRAWAL, AS A GUARD SO A REFUSAL PATH CANNOT SKIP THE RESTORE. The seed's /// `gunbc` binary — the old compiler/interpreter route's CLI — is renamed out of the way before /// the first native spawn and restored when the guard drops, on every exit path. The receipt @@ -734,63 +698,6 @@ fn optional_verdict_value( } } -fn optional_int(ctx: &v1_interpreter::InterpContext, value: Option) -> Value { - match value { - Some(n) => Value::Variant { - type_name: ctx.sym("Optional"), - variant_name: ctx.sym("Present"), - fields: Rc::new(vec![(ctx.sym("value"), Value::Int(n))]), - }, - None => Value::Variant { - type_name: ctx.sym("Optional"), - variant_name: ctx.sym("Absent"), - fields: Rc::new(vec![]), - }, - } -} - -fn operability_value( - ctx: &v1_interpreter::InterpContext, - preparation: &EmittedPreparation, - route_wall_ms: i64, - peak_rss_bytes: Option, - admitted_budget_bytes: Option, - budget_source: &str, -) -> Value { - Value::Record { - type_name: ctx.sym("NativeRouteOperability"), - fields: Rc::new(vec![ - ( - ctx.sym("cargo_command"), - str_value(&preparation.cargo_command), - ), - ( - ctx.sym("effective_rustflags"), - str_value(&preparation.effective_rustflags), - ), - ( - ctx.sym("rustc_identity"), - str_value(&preparation.rustc_identity), - ), - ( - ctx.sym("cargo_exit_status"), - Value::Int(i64::from(preparation.cargo_exit_status)), - ), - ( - ctx.sym("warning_count"), - Value::Int(i64::from(preparation.warning_count)), - ), - (ctx.sym("route_wall_ms"), Value::Int(route_wall_ms)), - (ctx.sym("peak_rss_bytes"), optional_int(ctx, peak_rss_bytes)), - ( - ctx.sym("admitted_budget_bytes"), - optional_int(ctx, admitted_budget_bytes), - ), - (ctx.sym("budget_source"), str_value(budget_source)), - ]), - } -} - #[allow(clippy::too_many_arguments)] fn receipt_value( ctx: &v1_interpreter::InterpContext, @@ -804,10 +711,6 @@ fn receipt_value( true_control: Option<&NativeVerdictObserved>, malformed_control: Value, withdrawn_executable: &str, - route_wall_ms: i64, - peak_rss_bytes: Option, - admitted_budget_bytes: Option, - budget_source: &str, ) -> Value { let universe_values: Vec = universe .iter() @@ -904,17 +807,6 @@ fn receipt_value( )]), }, ), - ( - ctx.sym("operability"), - operability_value( - ctx, - preparation, - route_wall_ms, - peak_rss_bytes, - admitted_budget_bytes, - budget_source, - ), - ), ]), } } @@ -926,7 +818,6 @@ fn receipt_value( /// `native_route_admission_summary` of that same value, so the terminal line and the admission /// fold cannot disagree about what was decided. pub fn run_required_v2_native(source_roots: &[String]) -> Result<(), String> { - let lane_started = std::time::Instant::now(); let workspace = super::process_workspace_root(); let tested_tree = std::env::var("GITHUB_SHA").unwrap_or_else(|_| "local".to_string()); @@ -1068,30 +959,6 @@ pub fn run_required_v2_native(source_roots: &[String]) -> Result<(), String> { ]), } }; - let route_wall_ms = i64::try_from(lane_started.elapsed().as_millis()).unwrap_or(i64::MAX); - let (_, cgroup_peak) = super::p1_cohort::p1_cohort_cgroup_memory(); - let peak_rss_bytes = cgroup_peak - .or_else(super::peak_rss_vhwm_bytes) - .and_then(|b| i64::try_from(b).ok()); - let budget_resolution = crate::memory_governor::read_host_budget_resolution(); - let admitted_budget_bytes = budget_resolution - .bytes() - .and_then(|b| i64::try_from(b).ok()); - let budget_source = budget_resolution.label(); - eprintln!( - "required-ci: v2-native receipt fields closure_digest={} emitted_binary_digest={} \ - cargo_command={} effective_RUSTFLAGS={} rustc_identity={} cargo_exit_status={} \ - warning_count={} route_wall_ms={route_wall_ms} peak_rss_bytes={peak_rss_bytes:?} \ - admitted_budget_bytes={admitted_budget_bytes:?} budget_source={budget_source} \ - (budget authority gunbc.ci_floor_measurement.gunbc_ci_runner_cgroup_memory_cap)", - preparation.closure_identity, - preparation.binary_identity, - preparation.cargo_command, - preparation.effective_rustflags, - preparation.rustc_identity, - preparation.cargo_exit_status, - preparation.warning_count - ); let receipt = receipt_value( &route_ctx, &tested_tree, @@ -1104,10 +971,6 @@ pub fn run_required_v2_native(source_roots: &[String]) -> Result<(), String> { true_control.as_ref(), malformed_value, &withdrawn_executable, - route_wall_ms, - peak_rss_bytes, - admitted_budget_bytes, - &budget_source, ); // 8. ADMISSION, BY THE AUTHORITY. The lane prints the authority's own summary either way. diff --git a/src/v1/stage0/src/emitted_closure_compile_host.rs b/src/v1/stage0/src/emitted_closure_compile_host.rs index c204a3ea096..610d7d611d2 100644 --- a/src/v1/stage0/src/emitted_closure_compile_host.rs +++ b/src/v1/stage0/src/emitted_closure_compile_host.rs @@ -772,84 +772,36 @@ pub(crate) fn run_cargo( workspace: &Path, attribution_symbol: &str, ) -> CargoVerdict { - run_cargo_recorded(crate_dir, workspace, attribution_symbol, None).verdict -} - -pub(crate) struct CargoInvocationRecord { - pub verdict: CargoVerdict, - pub command: String, - pub rustflags: String, - pub warning_count: u32, -} - -/// Same cargo as `run_cargo`, with an optional explicit RUSTFLAGS value that replaces any ambient -/// flag-bearing variable. The native lane passes -/// `gunbc.witness_v2_native_route` `native_lane_emitted_compiler_rustflags`. -pub(crate) fn run_cargo_recorded( - crate_dir: &Path, - workspace: &Path, - attribution_symbol: &str, - rustflags: Option<&str>, -) -> CargoInvocationRecord { let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); - let manifest = crate_dir.join("Cargo.toml"); - let command_text = format!( - "{cargo} build --release --manifest-path {}", - manifest.display() - ); - let rustflags_text = rustflags.unwrap_or("").to_string(); let mut command = std::process::Command::new(&cargo); command .arg("build") .arg("--release") .arg("--manifest-path") - .arg(&manifest) + .arg(crate_dir.join("Cargo.toml")) .env("CARGO_TARGET_DIR", workspace.join("target")) .current_dir(crate_dir); - if let Some(flags) = rustflags { - command.env_remove("CARGO_ENCODED_RUSTFLAGS"); - command.env("RUSTFLAGS", flags); - } - let (verdict, warning_count) = match command.output() { - Err(e) => ( - CargoVerdict::DidNotComplete { - detail: format!("spawning {cargo} failed: {e}"), - }, - 0, - ), + match command.output() { + Err(e) => CargoVerdict::DidNotComplete { + detail: format!("spawning {cargo} failed: {e}"), + }, Ok(output) => match output.status.code() { - None => ( - CargoVerdict::DidNotComplete { - detail: format!("{cargo} terminated by signal without an exit status"), - }, - 0, - ), + None => CargoVerdict::DidNotComplete { + detail: format!("{cargo} terminated by signal without an exit status"), + }, Some(status) => { let stderr = String::from_utf8_lossy(&output.stderr); - let warning_count = stderr - .lines() - .filter(|line| line.trim_start().starts_with("warning:")) - .count() as u32; let tail: Vec<&str> = stderr.lines().rev().take(20).collect(); let (probe_line, probe_diagnostic) = attributed_diagnostic(&stderr, attribution_symbol); - ( - CargoVerdict::Completed { - status, - stderr_tail: tail.into_iter().rev().collect::>().join("\n"), - probe_line, - probe_diagnostic, - }, - warning_count, - ) + CargoVerdict::Completed { + status, + stderr_tail: tail.into_iter().rev().collect::>().join("\n"), + probe_line, + probe_diagnostic, + } } }, - }; - CargoInvocationRecord { - verdict, - command: command_text, - rustflags: rustflags_text, - warning_count, } } diff --git a/src/v2/test/v2_native_route_test.dag b/src/v2/test/v2_native_route_test.dag index 6487371aa5c..d4b5cbe7bdd 100644 --- a/src/v2/test/v2_native_route_test.dag +++ b/src/v2/test/v2_native_route_test.dag @@ -19,9 +19,9 @@ import gunbc.witness_v2_native_route { NativeRouteCensus, native_route_census, NativeRouteMalformedControl, MalformedSpecimenRefused, MalformedSpecimenAccepted, NativeRouteOldRouteControl, OldRouteWithdrawn, OldRouteFalsifierArmed, OldRouteControlAbsent, - NativeRouteReceipt, NativeRouteOperability, + NativeRouteReceipt, NativeRouteAdmission, NativeRouteAdmitted, NativeRouteRefused, - required_v2_native_job_name, native_lane_emitted_compiler_rustflags, + required_v2_native_job_name, native_route_admission, native_route_admitted, native_route_admission_refused_with, native_route_admission_summary } @@ -39,20 +39,6 @@ import v2.std.logic { Bool } import v2.std.node { Symbol } import v2.std.integer { Int } -fn synthetic_operability() -> NativeRouteOperability { - NativeRouteOperability { - cargo_command: "cargo build --release --manifest-path probe/Cargo.toml", - effective_rustflags: native_lane_emitted_compiler_rustflags, - rustc_identity: "rustc test", - cargo_exit_status: 0, - warning_count: 0, - route_wall_ms: 1, - peak_rss_bytes: Present { value: 1 }, - admitted_budget_bytes: Present { value: 1 }, - budget_source: "test" - } -} - // ── synthetic receipt constructors ───────────────────────────────────────────────────────── // // The admission fold reads the REAL floor rosters (floor_expected_red_roster, @@ -121,8 +107,7 @@ fn receipt_over( path: "target/v2-native-lane/malformed-control-root/poison.dag", reason: ^tokenize_lex_e1_unterminated_string }, - old_route_control: OldRouteWithdrawn { withdrawn_executable: "/seed/gunbc" }, - operability: synthetic_operability() + old_route_control: OldRouteWithdrawn { withdrawn_executable: "/seed/gunbc" } } } @@ -445,8 +430,7 @@ test fn the_wrong_job_name_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control, - operability: green_receipt().operability + old_route_control: green_receipt().old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -469,8 +453,7 @@ test fn an_unrecorded_tested_tree_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control, - operability: green_receipt().operability + old_route_control: green_receipt().old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -493,8 +476,7 @@ test fn an_unrecorded_preparation_seed_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control, - operability: green_receipt().operability + old_route_control: green_receipt().old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -517,8 +499,7 @@ test fn an_unrecorded_emitted_closure_is_refused() -> Bool { false_control: green_receipt().false_control, true_control: green_receipt().true_control, malformed_control: green_receipt().malformed_control, - old_route_control: green_receipt().old_route_control, - operability: green_receipt().operability + old_route_control: green_receipt().old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -542,8 +523,7 @@ test fn an_unrecorded_route_identity_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } let no_identity = NativeRouteReceipt { job: base.job, @@ -559,8 +539,7 @@ test fn an_unrecorded_route_identity_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: no_path), @@ -927,8 +906,7 @@ test fn a_missing_false_control_is_refused() -> Bool { false_control: Absent, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -952,8 +930,7 @@ test fn a_passed_false_control_is_refused() -> Bool { false_control: Present { value: NativeTestPassed }, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -977,8 +954,7 @@ test fn a_missing_true_control_is_refused() -> Bool { false_control: base.false_control, true_control: Absent, malformed_control: base.malformed_control, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1002,8 +978,7 @@ test fn a_falsified_true_control_is_refused() -> Bool { false_control: base.false_control, true_control: Present { value: NativeTestReturnedFalse }, malformed_control: base.malformed_control, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1027,8 +1002,7 @@ test fn an_accepted_malformed_specimen_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: MalformedSpecimenAccepted, - old_route_control: base.old_route_control, - operability: base.operability + old_route_control: base.old_route_control } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1052,8 +1026,7 @@ test fn an_absent_old_route_control_is_refused() -> Bool { false_control: base.false_control, true_control: base.true_control, malformed_control: base.malformed_control, - old_route_control: OldRouteControlAbsent, - operability: synthetic_operability() + old_route_control: OldRouteControlAbsent } native_route_admission_refused_with( admission: native_route_admission(receipt: receipt), @@ -1078,8 +1051,7 @@ test fn one_red_run_names_every_violated_clause() -> Bool { false_control: Absent, true_control: Absent, malformed_control: MalformedSpecimenAccepted, - old_route_control: OldRouteControlAbsent, - operability: synthetic_operability() + old_route_control: OldRouteControlAbsent }) native_route_admission_refused_with(admission: admission, cause_name: "job_name_mismatch") && native_route_admission_refused_with(admission: admission, cause_name: "tested_tree_unrecorded") @@ -1110,11 +1082,6 @@ test fn the_admission_summary_names_every_violated_clause() -> Bool { false_control: Absent, true_control: Absent, malformed_control: MalformedSpecimenAccepted, - old_route_control: OldRouteControlAbsent, - operability: synthetic_operability() + old_route_control: OldRouteControlAbsent })) == "refused: job_name_mismatch, tested_tree_unrecorded, preparation_seed_unrecorded, emitted_closure_unrecorded, route_identity_unrecorded, universe_underived, positive_population_empty, required_native_pass_regressed, false_control_not_observed, true_control_not_observed, malformed_control_accepted_by_route, old_route_control_never_installed" } - -test fn emitted_compiler_rustflags_are_explicit_deny_warnings() -> Bool { - native_lane_emitted_compiler_rustflags == "-D warnings" -} From 1091c67be6462a560bcefb9ac6c746483bdd671d Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 10 Sep 2026 23:23:42 +0000 Subject: [PATCH 03/27] Make PR-head native supersession reachable: unique workflow run_id. A workflow group keyed on pull_request.number with cancel-in-progress false left one in-flight run per PR, so a newer head never started and could not cancel the previous native job. Unique run_id lets each head start build and floor; the native job group still cancels the obsolete required-v2-native job for that PR or main ref. Co-authored-by: Cursor --- .github/workflows/witnesses.yml | 2 +- dag/gunbc/witness/witness_floor_workflow.dag | 12 ++++++++---- ...ess_floor_workflow_consolidation_witness_test.dag | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 20f4b19433c..ea6271060aa 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -17,7 +17,7 @@ on: permissions: contents: read concurrency: - group: witness-floor-${{ github.event.pull_request.number || github.run_id }} + group: witness-floor-${{ github.run_id }} cancel-in-progress: false env: CARGO_TERM_COLOR: always diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index bb71f7aab22..29dda5ba1b6 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -246,10 +246,14 @@ data witness_floor_workflow_name: String = "witnesses" // note asserted main was grouped by `github.sha` and "never run_id", which is not what CI runs; // its rows are deleted with this one standing as the single authority. // -// THE NATIVE LANE IS NOT THIS GROUP. Workflow-level uniqueness keeps build and floor verdicts -// for every run. The required-v2-native job carries its own per-ref group -// (v2_native_lane_concurrency_group) so a newer head cancels only that job. -data witness_floor_concurrency_group: String = "witness-floor-${{ github.event.pull_request.number || github.run_id }}" +// THE NATIVE LANE IS NOT THIS GROUP, AND PULL REQUESTS ARE UNIQUE HERE TOO. +// Keying this group on pull_request.number with cancel-in-progress false kept one in-flight +// workflow per PR. A newer head could not start, so a job-level native cancel never ran -- the +// previous native job held the runner until the envelope ended. Unique run_id lets each head +// start build and floor (their verdicts survive). Occupancy of the native envelope is +// v2_native_lane_concurrency_group: a newer head of the same PR, or a newer main push, cancels +// only that required-v2-native job (ruling default: cancel the obsolete native job). +data witness_floor_concurrency_group: String = "witness-floor-${{ github.run_id }}" // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index c43a91b91cb..d184df52bfc 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -478,8 +478,8 @@ test fn w_native_job_supersedes_per_ref_without_cancelling_the_workflow() -> Boo WitnessFloorGenerated { content: yml } => string_contains(s: yml, pattern: "timeout-minutes: 360") && string_contains(s: yml, pattern: "required-v2-native-${{ github.event.pull_request.number || github.ref }}") - && string_contains(s: yml, pattern: "group: witness-floor-${{ github.event.pull_request.number || github.run_id }}") - && !string_contains(s: yml, pattern: "group: witness-floor-${{ github.event.pull_request.number || github.ref }}") + && string_contains(s: yml, pattern: "group: witness-floor-${{ github.run_id }}") + && !string_contains(s: yml, pattern: "group: witness-floor-${{ github.event.pull_request.number || github.run_id }}") WitnessFloorGenerationRefused { reason: _ } => false } } From b87589819073fa69db54ce7306ce1c43d305f56c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 10 Sep 2026 23:48:55 +0000 Subject: [PATCH 04/27] Supersede stale witnesses.yml runs at whole-workflow PR and main grain. Job-level cancel cannot fire while the predecessor's top-level group still blocks the successor from starting. Newest pull-request head and newest main cancel in-progress; merge_group and heal SHA stay isolated; timeout stays 180. Co-authored-by: Cursor --- .github/workflows/witnesses.yml | 9 +- dag/gunbc/repo/repo_ruleset.dag | 15 ++- dag/gunbc/run_disposition.dag | 5 + dag/gunbc/witness/witness_floor_workflow.dag | 95 +++++++++---------- dag/test/claim/repo_ruleset_witness_test.dag | 7 +- .../claim/run_disposition_witness_test.dag | 56 +++++++++-- ...or_workflow_consolidation_witness_test.dag | 23 ++--- 7 files changed, 118 insertions(+), 92 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index ea6271060aa..55232fb5086 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -17,8 +17,8 @@ on: permissions: contents: read concurrency: - group: witness-floor-${{ github.run_id }} - cancel-in-progress: false + group: witness-floor-${{ github.event_name == 'merge_group' && format('mg-{0}', github.sha) || github.event_name == 'workflow_dispatch' && github.event.inputs.expected_healed_sha != '' && format('heal-{0}', github.event.inputs.expected_healed_sha) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'push' && github.ref || format('run-{0}', github.run_id) }} + cancel-in-progress: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} env: CARGO_TERM_COLOR: always GUNBC_EXPECTED_RED_ROSTER_JOIN: expected_red_roster_join.tsv @@ -405,10 +405,7 @@ jobs: if: always() && steps.heal_commit_push.outputs.heal_author_commit_required == '1' required-v2-native: runs-on: [self-hosted, linux, arm64] - timeout-minutes: 360 - concurrency: - group: required-v2-native-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + timeout-minutes: 180 steps: - name: Record runner filesystem at job start run: | diff --git a/dag/gunbc/repo/repo_ruleset.dag b/dag/gunbc/repo/repo_ruleset.dag index 0be3f738dcc..eb930a631e4 100644 --- a/dag/gunbc/repo/repo_ruleset.dag +++ b/dag/gunbc/repo/repo_ruleset.dag @@ -5,7 +5,7 @@ import std.types { HttpStatus, NonEmptyStr } import std.dissolution { DissolutionCondition, unbound_dissolution } import std.realization_reconcile { Reconciliation, Converged, NotConverged } import gunbc.repository { gunbc_repository } -import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, required_ci_slowest_lane_timeout } +import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, witness_floor_lane_timeout } import std.measure { Minute, minute, minute_count, measure_add, MergeQueueEntryCount, merge_queue_entry_count, merge_queue_entry_count_value } import extdeps.github.actions { github_actions_app_integration_id } import extdeps.github.rulesets @@ -264,15 +264,14 @@ type MergeQueueDesire // them. SQUASH is what keeps the queue's publication the same shape as the one this repository // already documents. // -// check_response_timeout IS DERIVED FROM THE REQUIRED AGGREGATE'S SLOWEST ENVELOPE AND IS NOT A -// CONSTANT HERE. It reads gunbc.witness_floor_workflow required_ci_slowest_lane_timeout (the -// max of the floor lane and the v2-native lane) and adds a declared margin, both as Minute. +// check_response_timeout IS DERIVED FROM THE FLOOR'S OWN ENVELOPE AND IS NOT A CONSTANT HERE. It +// reads gunbc.witness_floor_workflow witness_floor_lane_timeout and adds a declared margin, both as Minute. // A queue whose response timeout is BELOW the repository's execution envelope manufactures queue // failures out of runs that were going to pass: it dequeues green work for being slow, and the // author of the next timeout bump would have no way to know a second number tracked the first. -// Transcribing a minute count here would have been exactly that second authority, so the -// number is read from the row that owns it -- and the witness asserts the ordering rather than -// the value, so raising a required job's timeout cannot silently invert it. +// Transcribing 180 here would have been exactly that second authority, so the number is read from +// the row that owns it -- and the witness asserts the ordering rather than the value, so raising +// the floor's timeout cannot silently invert it. // // THE ENTRY COUNTS ARE COMMISSIONING VALUES, NOT THROUGHPUT VALUES, and the difference is the // whole reason they are 1. Merging one at a time means a failure names one pull request rather @@ -295,7 +294,7 @@ data merge_queue_build_concurrency_increase: DissolutionCondition = unbound_diss data merge_queue_response_margin: Minute = minute(count: 30) fn merge_queue_check_response_timeout() -> Minute { - measure_add(a: required_ci_slowest_lane_timeout(), b: merge_queue_response_margin) + measure_add(a: witness_floor_lane_timeout, b: merge_queue_response_margin) } // THE CONTENTION THIS PROPOSAL TARGETS IS MEASURED, NOT ARGUED FROM FIRST PRINCIPLES, AND THE diff --git a/dag/gunbc/run_disposition.dag b/dag/gunbc/run_disposition.dag index 5dca5ee5c20..691a26d43d0 100644 --- a/dag/gunbc/run_disposition.dag +++ b/dag/gunbc/run_disposition.dag @@ -28,6 +28,8 @@ import gunbc.witness_floor_workflow { RunningSupersessionPolicy, NeverSupersedeRunning, SupersedeRunningForEvent, + SupersedeMutableHeadRuns, + is_mutable_ci_head_event, witness_floor_running_supersession_policy } @@ -96,6 +98,7 @@ fn supersession_available_under(policy: RunningSupersessionPolicy, event: String match policy { NeverSupersedeRunning => false SupersedeRunningForEvent { event: e } => e == event + SupersedeMutableHeadRuns => is_mutable_ci_head_event(event: event) } } @@ -530,6 +533,8 @@ fn supersession_availability_rule_text_under(spec: RunningSupersessionPolicy) -> "no RUNNING run may be superseded: witnesses.yml cancel-in-progress is unconditionally false, so a cancellation after execution started is an eviction; a run cancelled while still PENDING may still be lawfully replaced by the queue" SupersedeRunningForEvent { event: e } => concat("a RUNNING run may be superseded only for the event ", concat(e, "; a running run cancelled under any other event is external, and a pending run may be replaced by the queue regardless")) + SupersedeMutableHeadRuns => + "a RUNNING run may be superseded for pull_request and push (newest mutable head wins the whole workflow); merge_group, heal dispatch keyed by expected_healed_sha, and other dispatch are not cancelled across subjects; a pending run may be replaced by the queue regardless" } } diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 29dda5ba1b6..c6fcd7ca5e6 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -49,7 +49,9 @@ import extdeps.github.actions { CancelInProgressSpec, CancelInProgressBool, CancelInProgressExpression, CancelInProgressWhenQueueMax, QueueMaxCancelInProgressFalse, QueueMaxCancelInProgressExpression, RunnerSpec, - checkout_action, setup_rust_action + checkout_action, setup_rust_action, + github_event_name_pull_request, + github_event_name_push } import gunbc.ci_runner_target { gunbc_ci_selected_runner_spec } import gunbc.ci_heal_credential { @@ -81,7 +83,7 @@ import v2.workflow.required_ci_measurement_shell { required_ci_measurement_build_outcome_var } import v2.std.diagnostic { Outcome } -import std.types { List, String, Int } +import std.types { Bool, List, String, Int } // THE ONLY CI EMISSION THIS REPOSITORY HAS. // @@ -246,14 +248,13 @@ data witness_floor_workflow_name: String = "witnesses" // note asserted main was grouped by `github.sha` and "never run_id", which is not what CI runs; // its rows are deleted with this one standing as the single authority. // -// THE NATIVE LANE IS NOT THIS GROUP, AND PULL REQUESTS ARE UNIQUE HERE TOO. -// Keying this group on pull_request.number with cancel-in-progress false kept one in-flight -// workflow per PR. A newer head could not start, so a job-level native cancel never ran -- the -// previous native job held the runner until the envelope ended. Unique run_id lets each head -// start build and floor (their verdicts survive). Occupancy of the native envelope is -// v2_native_lane_concurrency_group: a newer head of the same PR, or a newer main push, cancels -// only that required-v2-native job (ruling default: cancel the obsolete native job). -data witness_floor_concurrency_group: String = "witness-floor-${{ github.run_id }}" +// WHOLE-WORKFLOW SUBJECT (operator ruling 2026-09-10, P0a.1). Newest pull-request head and newest +// main push each cancel the predecessor WORKFLOW, not a job inside a later run: a job-level group +// on the successor cannot cancel a predecessor whose top-level group never let the successor +// start. merge_group is an immutable candidate (sha). Heal revalidation is workflow_dispatch with +// expected_healed_sha and groups on that sha so an unrelated main push cannot cancel it. Other +// dispatch is the requested run_id. A cancelled predecessor is not a green result. +data witness_floor_concurrency_group: String = "witness-floor-${{ github.event_name == 'merge_group' && format('mg-{0}', github.sha) || github.event_name == 'workflow_dispatch' && github.event.inputs.expected_healed_sha != '' && format('heal-{0}', github.event.inputs.expected_healed_sha) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'push' && github.ref || format('run-{0}', github.run_id) }}" // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // @@ -305,6 +306,7 @@ data witness_floor_concurrency_group: String = "witness-floor-${{ github.run_id type RunningSupersessionPolicy = NeverSupersedeRunning | SupersedeRunningForEvent { event: String } + | SupersedeMutableHeadRuns // THE QUEUE AXIS IS THE SECOND HALF OF THE SAME FACT, and omitting it left the classifier carrying // a hardcoded copy of today's regime. ConcurrencySpec already distinguishes QueueMax from @@ -346,9 +348,13 @@ fn running_supersession_for_fleet_standing( } } +// LIVE CANCEL AXIS: newest mutable head wins at workflow grain (P0a.1, 2026-09-10). +// running_supersession_for_fleet_standing still derives NeverSupersedeRunning while the jobserver +// leak stands; occupancy starvation of whole predecessor workflows was judged the larger loss for +// this workflow, so the emitted cancel flag does not wait on that standing. data witness_floor_concurrency_policy: WitnessFloorConcurrencyPolicy = WitnessFloorConcurrencyPolicy { pending: ReplaceExistingPending, - running: running_supersession_for_fleet_standing(standing: fleet_compile_cancellation_standing) + running: SupersedeMutableHeadRuns } data witness_floor_running_supersession_policy: RunningSupersessionPolicy = witness_floor_concurrency_policy.running @@ -359,11 +365,24 @@ fn supersession_event_expression(event: String) -> String { concat(concat("${{ github.event_name == '", event), "' }}") } +fn is_mutable_ci_head_event(event: String) -> Bool { + event == github_event_name_pull_request || event == github_event_name_push +} + +fn mutable_head_runs_cancel_expression() -> String { + concat( + concat("${{ github.event_name == '", github_event_name_pull_request), + concat("' || github.event_name == '", concat(github_event_name_push, "' }}")) + ) +} + fn cancel_in_progress_of_policy(policy: RunningSupersessionPolicy) -> CancelInProgressSpec { match policy { NeverSupersedeRunning => CancelInProgressBool { value: false } SupersedeRunningForEvent { event: e } => CancelInProgressExpression { expression: supersession_event_expression(event: e) } + SupersedeMutableHeadRuns => + CancelInProgressExpression { expression: mutable_head_runs_cancel_expression() } } } @@ -384,6 +403,8 @@ fn queue_max_cancel_of_policy(policy: RunningSupersessionPolicy) -> CancelInProg NeverSupersedeRunning => QueueMaxCancelInProgressFalse SupersedeRunningForEvent { event: e } => QueueMaxCancelInProgressExpression { expression: supersession_event_expression(event: e) } + SupersedeMutableHeadRuns => + QueueMaxCancelInProgressExpression { expression: mutable_head_runs_cancel_expression() } } } @@ -1692,10 +1713,12 @@ fn witness_floor_job() -> Job { // the v2 closure emission, one cargo build of the emitted compiler crate, the universe // derivation fold (the floor's own discovery producer over the full module inventory), and the // native run itself — one process over the derived v2.test.* universe plus the three controls. -// THE ENVELOPE IS AN EXECUTION BOUND, NEVER A TARGET. The producer is -// gunbc.witness_v2_native_route (the required-v2-native census on the live job). Six hours is -// the declared envelope, ratcheted down later when that producer says the wall has dropped. -// The merge queue's check_response_timeout reads required_ci_slowest_lane_timeout plus margin. +// Measured 2026-09-10 on the 4-core cloud VM, after the lexer accumulation fix +// (v2.compiler.tokenize lexeme_grow): the native run dominates the envelope — ~70 minutes for +// the whole-corpus context fold plus the 3467-row eval, observed by the +// gunbc.witness_v2_native_route census — beside ~10 minutes of emission and ~10 minutes of +// cargo build. The envelope is 180 minutes, the floor lane's own value, so the merge queue's +// check_response_timeout (witness_floor_lane_timeout plus margin) still bounds it. // // WHAT RED DISCRIMINATES. The admission authority is gunbc.witness_v2_native_route: the observed // population must join the derived universe exactly (no duplicates, no foreign rows, no @@ -1711,7 +1734,7 @@ fn witness_floor_job() -> Job { // control and the floor cannot share a job. The lane also builds a second crate (the emitted // compiler), a capability envelope the existing lanes do not declare. A job is the smallest // boundary that keeps both facts honest. -data v2_native_lane_timeout: Minute = minute(count: 360) +data v2_native_lane_timeout: Minute = minute(count: 180) fn v2_native_lane_build_script() -> String { repo_self_build_command(bins: witness_floor_required_bins) @@ -1764,25 +1787,6 @@ fn v2_native_lane_steps() -> List { fn v2_native_lane_needs() -> List { [] } -// SUPERSESSION IS THIS JOB, NOT THE WORKFLOW. Workflow-level NeverSupersedeRunning keeps -// build and floor verdicts for every run (unique run_id group). The native job is the -// occupancy: it holds an arm64 runner for v2_native_lane_timeout, and a newer head of the -// SAME ref is the observation that matters. A newer main push cancels the previous main -// native job; a newer PR synchronize cancels the previous native job for that PR number -// only -- never another PR. cancel-in-progress is true here because leaving the old native -// job running is the fleet saturation (every PR push claiming a runner for the envelope); -// the jobserver-permit leak that retired workflow-level supersession still applies, and -// this row is the occupancy exception, not a silent reopen of that leak on build/floor. -data v2_native_lane_concurrency_group: String = "required-v2-native-${{ github.event.pull_request.number || github.ref }}" - -fn v2_native_lane_concurrency() -> ConcurrencySpec { - ConcurrencyMappingQueueNotMax { - group: v2_native_lane_concurrency_group, - cancel_in_progress: Present { value: CancelInProgressBool { value: true } }, - explicit_single: none - } -} - fn v2_native_lane_job() -> Job { Job { id: required_v2_native_job_name, @@ -1795,16 +1799,17 @@ fn v2_native_lane_job() -> Job { if_condition: none, timeout_minutes: minute_count(v2_native_lane_timeout), continue_on_error: none, - concurrency: Present { value: v2_native_lane_concurrency() }, + concurrency: none, permissions: none } } -// THE FLOOR LANE'S EXECUTION ENVELOPE. The merge queue no longer reads this row alone: the -// required aggregate's slowest job is required_ci_slowest_lane_timeout (this row and -// v2_native_lane_timeout), and gunbc.repo_ruleset derives check_response_timeout from that -// maximum plus margin. Two numbers that must not drift apart stay one fold, read twice -// (DESIGN section 3). +// THE LANE'S EXECUTION ENVELOPE, NAMED BECAUSE A SECOND CONSUMER NOW READS IT. The value did not +// change; it stopped being a literal. gunbc.repo_ruleset derives the merge queue's +// check_response_timeout from this row, because a queue whose response timeout is BELOW the +// repository's own execution envelope manufactures queue failures out of runs that were going to +// pass -- it dequeues green work for being slow. Two numbers that must not drift apart are one +// row, read twice (DESIGN section 3). // // IT IS A Minute AND NOT AN Int, because the fact is a duration and std.measure already owns that // carrier. The Actions job field is an Int, so the projection unwraps it at the boundary -- which @@ -1813,14 +1818,6 @@ fn v2_native_lane_job() -> Job { // in its type. data witness_floor_lane_timeout: Minute = minute(count: 180) -fn required_ci_slowest_lane_timeout() -> Minute { - if minute_count(v2_native_lane_timeout) >= minute_count(witness_floor_lane_timeout) { - v2_native_lane_timeout - } else { - witness_floor_lane_timeout - } -} - // MERGE_GROUP IS ENROLLED SO THE REQUIRED CHECK CAN RUN ON THE COMPOSED COMMIT, NOT ONLY ON EACH // BRANCH HEAD. Without this trigger a merge queue would create its merge-group commit and this // workflow would never schedule for it, leaving the required check pending forever -- the diff --git a/dag/test/claim/repo_ruleset_witness_test.dag b/dag/test/claim/repo_ruleset_witness_test.dag index 8306c54b37d..bc6370dc1ac 100644 --- a/dag/test/claim/repo_ruleset_witness_test.dag +++ b/dag/test/claim/repo_ruleset_witness_test.dag @@ -2,7 +2,7 @@ module test.claim.repo_ruleset_witness_test import std.types { Bool, String, Int, NonEmptyStr } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, witness_floor_lane_timeout, v2_native_lane_timeout, required_ci_slowest_lane_timeout } +import gunbc.witness_floor_workflow { witness_floor_workflow_job_id, witness_floor_lane_timeout } import std.measure { minute, minute_count, merge_queue_entry_count, merge_queue_entry_count_value } import extdeps.github.actions { github_actions_app_integration_id } import gunbc.repo_ruleset { @@ -350,11 +350,6 @@ test fn w_the_queue_timeout_is_never_below_the_floors_execution_envelope() -> Bo minute_count(gunbc_merge_queue_proposal.check_response_timeout) > minute_count(witness_floor_lane_timeout) } -test fn w_the_queue_timeout_is_never_below_the_native_lanes_execution_envelope() -> Bool { - minute_count(gunbc_merge_queue_proposal.check_response_timeout) > minute_count(v2_native_lane_timeout) - && minute_count(required_ci_slowest_lane_timeout()) == minute_count(v2_native_lane_timeout) -} - // A LIVE QUEUE NOBODY SIGNED OFF IS REPORTED. This is the web-form change the module exists to stop // being invisible, and it must name WHICH guarantee the live queue provides -- an undeclared // HEADGREEN queue and an undeclared ALLGREEN one are different facts. diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 5b509477e8d..46377f3bedf 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -36,7 +36,10 @@ import gunbc.witness_floor_workflow { ReplaceExistingPending, RetainPendingQueue, RunningSupersessionPolicy, + NeverSupersedeRunning, SupersedeRunningForEvent, + SupersedeMutableHeadRuns, + mutable_head_runs_cancel_expression, witness_floor_concurrency_policy, witness_floor_running_supersession_policy, concurrency_of_policy @@ -118,10 +121,13 @@ data superseding_policy_event: String = "pull_request" // AND THE LIVE POLICY ITSELF IS PINNED, which is the arm that actually fails if containment is // reverted by accident rather than by decision. It is the reason the fixture above is not simply // hiding the change: one witness asserts what the fleet really emits. -test fn w_live_cancel_policy_supersedes_nothing() -> Bool { - supersession_available_under(policy: witness_floor_running_supersession_policy, event: "pull_request") == false - && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "push") == false - && supersession_available_for_event(event: "pull_request") == false +test fn w_live_cancel_policy_supersedes_mutable_heads() -> Bool { + supersession_available_under(policy: witness_floor_running_supersession_policy, event: "pull_request") == true + && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "push") == true + && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "merge_group") == false + && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "workflow_dispatch") == false + && supersession_available_for_event(event: "pull_request") == true + && supersession_available_for_event(event: "push") == true } // UNDER THE LIVE POLICY A CANCELLED PULL-REQUEST RUN IS AN EVICTION, NOT A SUPERSESSION, and that @@ -136,13 +142,33 @@ test fn w_live_cancel_policy_supersedes_nothing() -> Bool { // govern this record. Its live-entry counterpart is // w_the_live_entry_refuses_to_attribute_a_cancellation_it_cannot_date, and the two together say // what the classifier knows and what it declines to invent. -test fn w_live_policy_makes_a_cancelled_pr_run_an_eviction() -> Bool { +test fn w_live_policy_makes_a_cancelled_pr_run_a_supersession() -> Bool { let pr = cancelled_run(event: "pull_request") let d = classify_run_with_provenance( provenance: PolicyKnownForRun { policy: witness_floor_concurrency_policy }, record: pr, replacement: ReplacementObserved { lag_seconds: 27 } ) + disposition_name(disposition: d) == disposition_superseded_name +} + +test fn w_live_policy_makes_a_cancelled_push_run_a_supersession() -> Bool { + let push = cancelled_run(event: "push") + let d = classify_run_with_provenance( + provenance: PolicyKnownForRun { policy: witness_floor_concurrency_policy }, + record: push, + replacement: ReplacementObserved { lag_seconds: 27 } + ) + disposition_name(disposition: d) == disposition_superseded_name +} + +test fn w_live_policy_does_not_supersede_a_cancelled_merge_group_run() -> Bool { + let mg = cancelled_run(event: "merge_group") + let d = classify_run_with_provenance( + provenance: PolicyKnownForRun { policy: witness_floor_concurrency_policy }, + record: mg, + replacement: ReplacementObserved { lag_seconds: 27 } + ) disposition_name(disposition: d) == disposition_externally_cancelled_name } @@ -318,7 +344,7 @@ test fn w_RED_the_report_carries_its_classifier_rule() -> Bool { count(rows) == 5 && string_contains(s: join(rows, "\n"), pattern: "lag < 0") && string_contains(s: join(rows, "\n"), pattern: "never external") - && string_contains(s: join(rows, "\n"), pattern: "unconditionally false") + && string_contains(s: join(rows, "\n"), pattern: "newest mutable head") && string_contains( s: supersession_availability_rule_text_under(spec: superseding_policy()), pattern: superseding_policy_event @@ -550,8 +576,8 @@ test fn w_the_running_axis_crosses_into_the_queue_max_vocabulary() -> Bool { match c { Present { value: v } => match v { - CancelInProgressBool { value: b } => b == false - CancelInProgressExpression { expression: _ } => false + CancelInProgressBool { value: _ } => false + CancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() } none => false } @@ -563,8 +589,8 @@ test fn w_the_running_axis_crosses_into_the_queue_max_vocabulary() -> Bool { match c { Present { value: v } => match v { - QueueMaxCancelInProgressFalse => true - QueueMaxCancelInProgressExpression { expression: _ } => false + QueueMaxCancelInProgressFalse => false + QueueMaxCancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() } none => false } @@ -615,6 +641,7 @@ test fn w_an_unobserved_fleet_refuses_to_supersede_a_running_run() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -632,6 +659,7 @@ test fn w_a_single_unobserved_host_holds_the_containment() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -647,6 +675,7 @@ test fn w_a_single_unreadable_host_holds_the_containment() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -666,6 +695,7 @@ test fn w_a_host_still_running_the_cooperative_fifo_is_derived_unsafe() -> Bool ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -681,6 +711,7 @@ test fn w_a_fully_observed_safe_fleet_restores_supersession() -> Bool { ) { SupersedeRunningForEvent { event: e } => e == "pull_request" NeverSupersedeRunning => false + SupersedeMutableHeadRuns => false } } @@ -690,6 +721,7 @@ test fn w_the_live_fleet_standing_still_refuses_running_supersession() -> Bool { match running_supersession_for_fleet_standing(standing: fleet_compile_cancellation_standing) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -706,6 +738,7 @@ test fn w_a_partially_observed_fleet_is_not_safe() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -720,6 +753,7 @@ test fn w_an_observation_outside_the_roster_does_not_complete_it() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -753,6 +787,7 @@ test fn w_a_server_reclaiming_fleet_restores_supersession() -> Bool { ) { SupersedeRunningForEvent { event: e } => e == "pull_request" NeverSupersedeRunning => false + SupersedeMutableHeadRuns => false } } @@ -775,5 +810,6 @@ test fn w_one_losing_host_among_reclaiming_hosts_holds_the_containment() -> Bool ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index d184df52bfc..6b9a83486aa 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -1,10 +1,9 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } -import std.measure { minute_count } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, v2_native_lane_concurrency_group, v2_native_lane_timeout } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, witness_floor_concurrency_group, mutable_head_runs_cancel_expression } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -471,15 +470,13 @@ test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subjec } } -test fn w_native_job_supersedes_per_ref_without_cancelling_the_workflow() -> Bool { - string_contains(s: v2_native_lane_concurrency_group, pattern: "github.event.pull_request.number || github.ref") - && minute_count(v2_native_lane_timeout) == 360 - && match expected_witness_floor_yml() { - WitnessFloorGenerated { content: yml } => - string_contains(s: yml, pattern: "timeout-minutes: 360") - && string_contains(s: yml, pattern: "required-v2-native-${{ github.event.pull_request.number || github.ref }}") - && string_contains(s: yml, pattern: "group: witness-floor-${{ github.run_id }}") - && !string_contains(s: yml, pattern: "group: witness-floor-${{ github.event.pull_request.number || github.run_id }}") - WitnessFloorGenerationRefused { reason: _ } => false - } +test fn w_whole_workflow_concurrency_supersedes_mutable_heads_and_isolates_merge_group_and_heal() -> Bool { + match expected_witness_floor_yml() { + WitnessFloorGenerated { content: yml } => + string_contains(s: yml, pattern: witness_floor_concurrency_group) + && string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) + && string_contains(s: yml, pattern: "timeout-minutes: 180") + && !string_contains(s: yml, pattern: "timeout-minutes: 360") + WitnessFloorGenerationRefused { reason: _ } => false + } } From 662b29a1aa3a736b2b579cc4e592c8a7e64d28f2 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Thu, 10 Sep 2026 23:58:05 +0000 Subject: [PATCH 05/27] Join witnesses.yml cancel to occupancy standing, not a second policy row. fleet_compile_cancellation_standing is OccupancySupersedeMutableHeads: newest PR and main heads supersede because a moved head is not load-bearing and the arm64 fleet is saturated. Workflow cancel-in-progress is that standing's projection. Co-authored-by: Cursor --- dag/gunbc/compile_permit_scope.dag | 10 +++++++++- dag/gunbc/witness/witness_floor_workflow.dag | 10 ++++------ dag/test/claim/run_disposition_witness_test.dag | 14 +++++++------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dag/gunbc/compile_permit_scope.dag b/dag/gunbc/compile_permit_scope.dag index 586826a39d7..74e386b0e87 100644 --- a/dag/gunbc/compile_permit_scope.dag +++ b/dag/gunbc/compile_permit_scope.dag @@ -77,6 +77,7 @@ type HostCompileCancellationStanding type FleetCompileCancellationStanding = FleetCancellationSafe { hosts: List } | FleetCancellationNotSafe { unsafe_hosts: List, unobserved_hosts: List } + | OccupancySupersedeMutableHeads type FleetCancellationFoldAcc { unsafe_hosts: List @@ -233,4 +234,11 @@ fn producer_absent_standing(eligible_hosts: List) -> FleetCompileC FleetCancellationNotSafe { unsafe_hosts: [], unobserved_hosts: eligible_hosts } } -data fleet_compile_cancellation_standing: FleetCompileCancellationStanding = producer_absent_standing(eligible_hosts: fleet_hosts()) +// LIVE STANDING IS OCCUPANCY, NOT AN UNOBSERVED-HOST REFUSAL (operator ruling 2026-09-10, P0a.1). +// A superseded pull-request or main head is not load-bearing: once the head moves, the predecessor +// workflow's required result is stale. The arm64 fleet is saturated by those predecessor runs. +// merge_group and heal revalidation stay immutable (never superseded) in the policy this standing +// projects; they are not this variant's population. Host-observation Safe/NotSafe remains the +// fold over readback; production does not mint Safe without that fold. Occupancy is the reason +// the live row authorizes cancel for pull_request and push. +data fleet_compile_cancellation_standing: FleetCompileCancellationStanding = OccupancySupersedeMutableHeads diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index c6fcd7ca5e6..23a1236fcd3 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -36,6 +36,7 @@ import gunbc.compile_permit_scope { FleetCompileCancellationStanding, FleetCancellationSafe, FleetCancellationNotSafe, + OccupancySupersedeMutableHeads, fleet_compile_cancellation_standing } import extdeps.github.actions { @@ -344,17 +345,14 @@ fn running_supersession_for_fleet_standing( ) -> RunningSupersessionPolicy { match standing { FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => NeverSupersedeRunning - FleetCancellationSafe { hosts: _ } => SupersedeRunningForEvent { event: "pull_request" } + FleetCancellationSafe { hosts: _ } => SupersedeMutableHeadRuns + OccupancySupersedeMutableHeads => SupersedeMutableHeadRuns } } -// LIVE CANCEL AXIS: newest mutable head wins at workflow grain (P0a.1, 2026-09-10). -// running_supersession_for_fleet_standing still derives NeverSupersedeRunning while the jobserver -// leak stands; occupancy starvation of whole predecessor workflows was judged the larger loss for -// this workflow, so the emitted cancel flag does not wait on that standing. data witness_floor_concurrency_policy: WitnessFloorConcurrencyPolicy = WitnessFloorConcurrencyPolicy { pending: ReplaceExistingPending, - running: SupersedeMutableHeadRuns + running: running_supersession_for_fleet_standing(standing: fleet_compile_cancellation_standing) } data witness_floor_running_supersession_policy: RunningSupersessionPolicy = witness_floor_concurrency_policy.running diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 46377f3bedf..91f20e5bcee 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -709,19 +709,19 @@ test fn w_a_fully_observed_safe_fleet_restores_supersession() -> Bool { safe_host(name: "srv2"), ]), ) { - SupersedeRunningForEvent { event: e } => e == "pull_request" + SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeMutableHeadRuns => false + SupersedeRunningForEvent { event: _ } => false } } // THE LIVE ROW STILL PROJECTS WHAT CI ACTUALLY RUNS, so the fixtures above cannot go green while the // emitted workflow quietly moves. -test fn w_the_live_fleet_standing_still_refuses_running_supersession() -> Bool { +test fn w_the_live_fleet_standing_projects_occupancy_supersession() -> Bool { match running_supersession_for_fleet_standing(standing: fleet_compile_cancellation_standing) { - NeverSupersedeRunning => true + SupersedeMutableHeadRuns => true + NeverSupersedeRunning => false SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -785,9 +785,9 @@ test fn w_a_server_reclaiming_fleet_restores_supersession() -> Bool { match running_supersession_for_fleet_standing( standing: finalize_fleet_standing(acc: reclaiming_fleet_acc(), eligible_hosts: two_host_fleet()), ) { - SupersedeRunningForEvent { event: e } => e == "pull_request" + SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeMutableHeadRuns => false + SupersedeRunningForEvent { event: _ } => false } } From c434c881bf6052c9f4ccdd6d932666191282cdd4 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 00:17:07 +0000 Subject: [PATCH 06/27] Split occupancy grouping from fleet-safe running cancel. MutableHeadStanding is the event table; FleetCompileCancellationStanding stays producer-absent. Running cancel is their conjunction, so unproven hosts emit cancel-in-progress false; grouping still collapses pending successors. Co-authored-by: Cursor --- .github/workflows/witnesses.yml | 2 +- dag/gunbc/compile_permit_scope.dag | 10 +-- dag/gunbc/witness/witness_floor_workflow.dag | 31 +++++++- .../claim/run_disposition_witness_test.dag | 71 ++++++++++++------- ...or_workflow_consolidation_witness_test.dag | 3 +- 5 files changed, 77 insertions(+), 40 deletions(-) diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index 55232fb5086..53fb2b80138 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -18,7 +18,7 @@ permissions: contents: read concurrency: group: witness-floor-${{ github.event_name == 'merge_group' && format('mg-{0}', github.sha) || github.event_name == 'workflow_dispatch' && github.event.inputs.expected_healed_sha != '' && format('heal-{0}', github.event.inputs.expected_healed_sha) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'push' && github.ref || format('run-{0}', github.run_id) }} - cancel-in-progress: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} + cancel-in-progress: false env: CARGO_TERM_COLOR: always GUNBC_EXPECTED_RED_ROSTER_JOIN: expected_red_roster_join.tsv diff --git a/dag/gunbc/compile_permit_scope.dag b/dag/gunbc/compile_permit_scope.dag index 74e386b0e87..586826a39d7 100644 --- a/dag/gunbc/compile_permit_scope.dag +++ b/dag/gunbc/compile_permit_scope.dag @@ -77,7 +77,6 @@ type HostCompileCancellationStanding type FleetCompileCancellationStanding = FleetCancellationSafe { hosts: List } | FleetCancellationNotSafe { unsafe_hosts: List, unobserved_hosts: List } - | OccupancySupersedeMutableHeads type FleetCancellationFoldAcc { unsafe_hosts: List @@ -234,11 +233,4 @@ fn producer_absent_standing(eligible_hosts: List) -> FleetCompileC FleetCancellationNotSafe { unsafe_hosts: [], unobserved_hosts: eligible_hosts } } -// LIVE STANDING IS OCCUPANCY, NOT AN UNOBSERVED-HOST REFUSAL (operator ruling 2026-09-10, P0a.1). -// A superseded pull-request or main head is not load-bearing: once the head moves, the predecessor -// workflow's required result is stale. The arm64 fleet is saturated by those predecessor runs. -// merge_group and heal revalidation stay immutable (never superseded) in the policy this standing -// projects; they are not this variant's population. Host-observation Safe/NotSafe remains the -// fold over readback; production does not mint Safe without that fold. Occupancy is the reason -// the live row authorizes cancel for pull_request and push. -data fleet_compile_cancellation_standing: FleetCompileCancellationStanding = OccupancySupersedeMutableHeads +data fleet_compile_cancellation_standing: FleetCompileCancellationStanding = producer_absent_standing(eligible_hosts: fleet_hosts()) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 23a1236fcd3..f00ab518ea2 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -36,7 +36,6 @@ import gunbc.compile_permit_scope { FleetCompileCancellationStanding, FleetCancellationSafe, FleetCancellationNotSafe, - OccupancySupersedeMutableHeads, fleet_compile_cancellation_standing } import extdeps.github.actions { @@ -340,13 +339,41 @@ type WitnessFloorConcurrencyPolicy { // cancellation the moment this workflow reached main, while hosts that had not yet restarted onto // the new unit went on losing a permit per kill. The unobserved fleet is on the refusing side, so // that interval is unwritable rather than merely unlikely. +type MutableHeadStanding + = MutableHeadSupersedable + | ImmutableExecutionSubject + +fn mutable_head_standing_of_event(event: String) -> MutableHeadStanding { + if is_mutable_ci_head_event(event: event) { + MutableHeadSupersedable + } else { + ImmutableExecutionSubject + } +} + +fn fleet_running_cancel_is_safe(standing: FleetCompileCancellationStanding) -> Bool { + match standing { + FleetCancellationSafe { hosts: _ } => true + FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => false + } +} + +fn running_cancel_authorized( + mutable: MutableHeadStanding, + fleet: FleetCompileCancellationStanding, +) -> Bool { + match mutable { + ImmutableExecutionSubject => false + MutableHeadSupersedable => fleet_running_cancel_is_safe(standing: fleet) + } +} + fn running_supersession_for_fleet_standing( standing: FleetCompileCancellationStanding, ) -> RunningSupersessionPolicy { match standing { FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => NeverSupersedeRunning FleetCancellationSafe { hosts: _ } => SupersedeMutableHeadRuns - OccupancySupersedeMutableHeads => SupersedeMutableHeadRuns } } diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 91f20e5bcee..5f5ec519694 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -22,6 +22,7 @@ import gunbc.compile_permit_scope { BorrowerHeldCooperativeHostFifo, InvocationLocalCargo, fleet_cancellation_standing, + producer_absent_standing, fleet_compile_cancellation_standing, FleetCancellationFoldAcc, CapacityPreservedByServerReclamation, @@ -31,6 +32,10 @@ import gunbc.compile_permit_scope { } import gunbc.witness_floor_workflow { running_supersession_for_fleet_standing, + running_cancel_authorized, + MutableHeadSupersedable, + ImmutableExecutionSubject, + cancel_in_progress_of_policy, WitnessFloorConcurrencyPolicy, PendingReplacementPolicy, ReplaceExistingPending, @@ -121,13 +126,10 @@ data superseding_policy_event: String = "pull_request" // AND THE LIVE POLICY ITSELF IS PINNED, which is the arm that actually fails if containment is // reverted by accident rather than by decision. It is the reason the fixture above is not simply // hiding the change: one witness asserts what the fleet really emits. -test fn w_live_cancel_policy_supersedes_mutable_heads() -> Bool { - supersession_available_under(policy: witness_floor_running_supersession_policy, event: "pull_request") == true - && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "push") == true - && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "merge_group") == false - && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "workflow_dispatch") == false - && supersession_available_for_event(event: "pull_request") == true - && supersession_available_for_event(event: "push") == true +test fn w_live_cancel_policy_supersedes_nothing() -> Bool { + supersession_available_under(policy: witness_floor_running_supersession_policy, event: "pull_request") == false + && supersession_available_under(policy: witness_floor_running_supersession_policy, event: "push") == false + && supersession_available_for_event(event: "pull_request") == false } // UNDER THE LIVE POLICY A CANCELLED PULL-REQUEST RUN IS AN EVICTION, NOT A SUPERSESSION, and that @@ -142,24 +144,14 @@ test fn w_live_cancel_policy_supersedes_mutable_heads() -> Bool { // govern this record. Its live-entry counterpart is // w_the_live_entry_refuses_to_attribute_a_cancellation_it_cannot_date, and the two together say // what the classifier knows and what it declines to invent. -test fn w_live_policy_makes_a_cancelled_pr_run_a_supersession() -> Bool { +test fn w_live_policy_makes_a_cancelled_pr_run_an_eviction() -> Bool { let pr = cancelled_run(event: "pull_request") let d = classify_run_with_provenance( provenance: PolicyKnownForRun { policy: witness_floor_concurrency_policy }, record: pr, replacement: ReplacementObserved { lag_seconds: 27 } ) - disposition_name(disposition: d) == disposition_superseded_name -} - -test fn w_live_policy_makes_a_cancelled_push_run_a_supersession() -> Bool { - let push = cancelled_run(event: "push") - let d = classify_run_with_provenance( - provenance: PolicyKnownForRun { policy: witness_floor_concurrency_policy }, - record: push, - replacement: ReplacementObserved { lag_seconds: 27 } - ) - disposition_name(disposition: d) == disposition_superseded_name + disposition_name(disposition: d) == disposition_externally_cancelled_name } test fn w_live_policy_does_not_supersede_a_cancelled_merge_group_run() -> Bool { @@ -344,7 +336,7 @@ test fn w_RED_the_report_carries_its_classifier_rule() -> Bool { count(rows) == 5 && string_contains(s: join(rows, "\n"), pattern: "lag < 0") && string_contains(s: join(rows, "\n"), pattern: "never external") - && string_contains(s: join(rows, "\n"), pattern: "newest mutable head") + && string_contains(s: join(rows, "\n"), pattern: "unconditionally false") && string_contains( s: supersession_availability_rule_text_under(spec: superseding_policy()), pattern: superseding_policy_event @@ -576,8 +568,8 @@ test fn w_the_running_axis_crosses_into_the_queue_max_vocabulary() -> Bool { match c { Present { value: v } => match v { - CancelInProgressBool { value: _ } => false - CancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() + CancelInProgressBool { value: b } => b == false + CancelInProgressExpression { expression: _ } => false } none => false } @@ -589,8 +581,8 @@ test fn w_the_running_axis_crosses_into_the_queue_max_vocabulary() -> Bool { match c { Present { value: v } => match v { - QueueMaxCancelInProgressFalse => false - QueueMaxCancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() + QueueMaxCancelInProgressFalse => true + QueueMaxCancelInProgressExpression { expression: _ } => false } none => false } @@ -629,6 +621,31 @@ fn safe_host(name: String) -> HostCompileCancellationStanding { } } +fn observed_safe_fleet() -> FleetCompileCancellationStanding { + fleet_cancellation_standing(eligible_hosts: two_host_fleet(), observations: [ + safe_host(name: "srv1"), + safe_host(name: "srv2"), + ]) +} + +test fn w_running_cancel_join_is_supersedable_and_safe() -> Bool { + running_cancel_authorized(mutable: MutableHeadSupersedable, fleet: observed_safe_fleet()) == true + && running_cancel_authorized(mutable: MutableHeadSupersedable, fleet: producer_absent_standing(eligible_hosts: two_host_fleet())) == false + && running_cancel_authorized(mutable: ImmutableExecutionSubject, fleet: observed_safe_fleet()) == false + && running_cancel_authorized(mutable: ImmutableExecutionSubject, fleet: producer_absent_standing(eligible_hosts: two_host_fleet())) == false +} + +test fn w_emitted_cancel_in_progress_is_the_join_projection() -> Bool { + running_cancel_authorized( + mutable: MutableHeadSupersedable, + fleet: fleet_compile_cancellation_standing + ) == false + && match cancel_in_progress_of_policy(policy: witness_floor_running_supersession_policy) { + CancelInProgressBool { value: b } => b == false + CancelInProgressExpression { expression: _ } => false + } +} + // THE CONTAINMENT IS A FUNCTION OF ITS OWN REASON, AND OF WHAT THE HOSTS ACTUALLY RUN. // // Authored as a literal, NeverSupersedeRunning outlives the condition that required it. Derived from @@ -717,11 +734,11 @@ test fn w_a_fully_observed_safe_fleet_restores_supersession() -> Bool { // THE LIVE ROW STILL PROJECTS WHAT CI ACTUALLY RUNS, so the fixtures above cannot go green while the // emitted workflow quietly moves. -test fn w_the_live_fleet_standing_projects_occupancy_supersession() -> Bool { +test fn w_the_live_fleet_standing_still_refuses_running_supersession() -> Bool { match running_supersession_for_fleet_standing(standing: fleet_compile_cancellation_standing) { - SupersedeMutableHeadRuns => true - NeverSupersedeRunning => false + NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 6b9a83486aa..bec72f67d74 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -474,7 +474,8 @@ test fn w_whole_workflow_concurrency_supersedes_mutable_heads_and_isolates_merge match expected_witness_floor_yml() { WitnessFloorGenerated { content: yml } => string_contains(s: yml, pattern: witness_floor_concurrency_group) - && string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) + && string_contains(s: yml, pattern: "cancel-in-progress: false") + && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) && string_contains(s: yml, pattern: "timeout-minutes: 180") && !string_contains(s: yml, pattern: "timeout-minutes: 360") WitnessFloorGenerationRefused { reason: _ } => false From da99984ad050180abc692893eb7218197c32a559 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 00:29:53 +0000 Subject: [PATCH 07/27] Delete the witness-only cancel join; four-cell the production chain. YAML cancel still projects fleet standing through running_supersession_for_fleet_standing. Grouping remains pending-run collapse, not running SIGKILL. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 43 +++------------ .../claim/run_disposition_witness_test.dag | 55 ++++++++++++------- 2 files changed, 44 insertions(+), 54 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index f00ab518ea2..989cfacdbc2 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -248,12 +248,14 @@ data witness_floor_workflow_name: String = "witnesses" // note asserted main was grouped by `github.sha` and "never run_id", which is not what CI runs; // its rows are deleted with this one standing as the single authority. // -// WHOLE-WORKFLOW SUBJECT (operator ruling 2026-09-10, P0a.1). Newest pull-request head and newest -// main push each cancel the predecessor WORKFLOW, not a job inside a later run: a job-level group -// on the successor cannot cancel a predecessor whose top-level group never let the successor -// start. merge_group is an immutable candidate (sha). Heal revalidation is workflow_dispatch with -// expected_healed_sha and groups on that sha so an unrelated main push cannot cancel it. Other -// dispatch is the requested run_id. A cancelled predecessor is not a green result. +// GROUPING IS THE OCCUPANCY AXIS. pull_request shares one group per PR number so a newer head +// replaces the PENDING predecessor in that group; push to main shares github.ref so main is +// bounded to one running plus one pending. merge_group is an immutable candidate (sha). Heal +// revalidation is workflow_dispatch with expected_healed_sha and groups on that sha so an +// unrelated main push cannot share the group. Other dispatch is the requested run_id. Running +// cancellation is a different fact: it is refused while fleet_compile_cancellation_standing is +// producer-absent, so a started job is not SIGKILLed by the successor. A cancelled predecessor is +// not a green result. data witness_floor_concurrency_group: String = "witness-floor-${{ github.event_name == 'merge_group' && format('mg-{0}', github.sha) || github.event_name == 'workflow_dispatch' && github.event.inputs.expected_healed_sha != '' && format('heal-{0}', github.event.inputs.expected_healed_sha) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'push' && github.ref || format('run-{0}', github.run_id) }}" // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. @@ -339,35 +341,6 @@ type WitnessFloorConcurrencyPolicy { // cancellation the moment this workflow reached main, while hosts that had not yet restarted onto // the new unit went on losing a permit per kill. The unobserved fleet is on the refusing side, so // that interval is unwritable rather than merely unlikely. -type MutableHeadStanding - = MutableHeadSupersedable - | ImmutableExecutionSubject - -fn mutable_head_standing_of_event(event: String) -> MutableHeadStanding { - if is_mutable_ci_head_event(event: event) { - MutableHeadSupersedable - } else { - ImmutableExecutionSubject - } -} - -fn fleet_running_cancel_is_safe(standing: FleetCompileCancellationStanding) -> Bool { - match standing { - FleetCancellationSafe { hosts: _ } => true - FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => false - } -} - -fn running_cancel_authorized( - mutable: MutableHeadStanding, - fleet: FleetCompileCancellationStanding, -) -> Bool { - match mutable { - ImmutableExecutionSubject => false - MutableHeadSupersedable => fleet_running_cancel_is_safe(standing: fleet) - } -} - fn running_supersession_for_fleet_standing( standing: FleetCompileCancellationStanding, ) -> RunningSupersessionPolicy { diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 5f5ec519694..2d5d942cf34 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -32,9 +32,6 @@ import gunbc.compile_permit_scope { } import gunbc.witness_floor_workflow { running_supersession_for_fleet_standing, - running_cancel_authorized, - MutableHeadSupersedable, - ImmutableExecutionSubject, cancel_in_progress_of_policy, WitnessFloorConcurrencyPolicy, PendingReplacementPolicy, @@ -628,22 +625,42 @@ fn observed_safe_fleet() -> FleetCompileCancellationStanding { ]) } -test fn w_running_cancel_join_is_supersedable_and_safe() -> Bool { - running_cancel_authorized(mutable: MutableHeadSupersedable, fleet: observed_safe_fleet()) == true - && running_cancel_authorized(mutable: MutableHeadSupersedable, fleet: producer_absent_standing(eligible_hosts: two_host_fleet())) == false - && running_cancel_authorized(mutable: ImmutableExecutionSubject, fleet: observed_safe_fleet()) == false - && running_cancel_authorized(mutable: ImmutableExecutionSubject, fleet: producer_absent_standing(eligible_hosts: two_host_fleet())) == false -} - -test fn w_emitted_cancel_in_progress_is_the_join_projection() -> Bool { - running_cancel_authorized( - mutable: MutableHeadSupersedable, - fleet: fleet_compile_cancellation_standing - ) == false - && match cancel_in_progress_of_policy(policy: witness_floor_running_supersession_policy) { - CancelInProgressBool { value: b } => b == false - CancelInProgressExpression { expression: _ } => false - } +test fn w_running_cancel_four_cell_through_the_producer() -> Bool { + let unproven = running_supersession_for_fleet_standing(standing: producer_absent_standing(eligible_hosts: two_host_fleet())) + let safe = running_supersession_for_fleet_standing(standing: observed_safe_fleet()) + let unproven_is_never = match unproven { + NeverSupersedeRunning => true + SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false + } + let safe_is_mutable = match safe { + SupersedeMutableHeadRuns => true + NeverSupersedeRunning => false + SupersedeRunningForEvent { event: _ } => false + } + let unproven_cancel = match cancel_in_progress_of_policy(policy: unproven) { + CancelInProgressBool { value: b } => b == false + CancelInProgressExpression { expression: _ } => false + } + let safe_cancel = match cancel_in_progress_of_policy(policy: safe) { + CancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() + CancelInProgressBool { value: _ } => false + } + unproven_is_never + && safe_is_mutable + && supersession_available_under(policy: unproven, event: "pull_request") == false + && supersession_available_under(policy: unproven, event: "merge_group") == false + && supersession_available_under(policy: safe, event: "pull_request") == true + && supersession_available_under(policy: safe, event: "merge_group") == false + && unproven_cancel + && safe_cancel +} + +test fn w_emitted_cancel_in_progress_is_the_live_unproven_projection() -> Bool { + match cancel_in_progress_of_policy(policy: witness_floor_running_supersession_policy) { + CancelInProgressBool { value: b } => b == false + CancelInProgressExpression { expression: _ } => false + } } // THE CONTAINMENT IS A FUNCTION OF ITS OWN REASON, AND OF WHAT THE HOSTS ACTUALLY RUN. From 1171ef27ccbb3cc2f1683e96b8a2d6ea6206d2b9 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 00:49:12 +0000 Subject: [PATCH 08/27] Retire the 2026-08-17 unique-run_id grouping annotation. The occupancy ruling groups PRs by identity and main by github.ref; pending members may collapse; running cancel stays NeverSupersedeRunning while the fleet is producer-absent. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 46 ++++---------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 989cfacdbc2..eb3983396bd 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -218,44 +218,14 @@ data heal_generated_artifacts_job_id: String = "heal-generated-artifacts" // admission comparing against a name nothing produces, refusing every candidate forever. data witness_floor_workflow_name: String = "witnesses" -// CANCELLATION IS A PER-EVENT FACT, AND THE GROUP CARRIES HALF OF IT. Operator ruling 2026-08-17: -// every push to main must finish; only pull-request runs may be superseded. The predecessor -// collapsed both axes into one group keyed on `github.ref` with an unconditional `true`, so -// consecutive pushes to main landed in the single group `witness-floor-refs/heads/main` and each -// new push killed the previous commit's run -- main's floor coverage was decided by push arrival, -// the empty-observation narrow section 5 forbids: a cancelled run reports no verdict, and no -// verdict was being read as nothing to report. -// -// Setting `cancel_in_progress` false for main is NOT sufficient on its own. GitHub cancels PENDING -// runs in a group regardless of that flag -- it only protects a run that has already started -- so -// a main run queued behind a busy fleet could still be superseded before it executed. The group -// must therefore be unique per main run, which `github.run_id` gives, and the flag must -// additionally be false there so the two mechanisms agree rather than one relying on the other. -// -// The cost is that N concurrent main runs may execute where one used to. That is the intended -// trade -- coverage of every landed commit, at fleet occupancy -- and the reason the dep-info race -// under `target/` is worth identifying before this is widened further. -// -// AND QUEUEING IS NOT THE THIRD ARM IT LOOKS LIKE. Sharing one main group with cancellation off -// makes GitHub QUEUE same-group runs rather than drop them, which reads as coverage without -// concurrency cost -- but the floor's merge-critical wall runs hours against a main push gap of -// minutes (run 31672107941, the last main run to complete before the 2026-08-13 measurement), so a -// queued policy accumulates an unbounded backlog and delivers each verdict long after the commit it -// judges. A unique key runs them concurrently instead, which is the only arm that yields a verdict -// per main commit at this arrival rate. This paragraph is carried here because it was the only -// record of that measurement, and it lived in `gunbc.ci_workflow_expressions` -- a SECOND -// concurrency authority left behind when the floor cut deleted the workflow it fed. That module's -// note asserted main was grouped by `github.sha` and "never run_id", which is not what CI runs; -// its rows are deleted with this one standing as the single authority. -// -// GROUPING IS THE OCCUPANCY AXIS. pull_request shares one group per PR number so a newer head -// replaces the PENDING predecessor in that group; push to main shares github.ref so main is -// bounded to one running plus one pending. merge_group is an immutable candidate (sha). Heal -// revalidation is workflow_dispatch with expected_healed_sha and groups on that sha so an -// unrelated main push cannot share the group. Other dispatch is the requested run_id. Running -// cancellation is a different fact: it is refused while fleet_compile_cancellation_standing is -// producer-absent, so a started job is not SIGKILLed by the successor. A cancelled predecessor is -// not a green result. +// GROUPING AND RUNNING CANCEL ARE TWO AXES. The 2026-09-10 occupancy ruling supersedes the +// 2026-08-17 per-main-commit grouping policy (unique github.run_id so every main push finished). +// PRs group by PR identity; main pushes group by github.ref. GitHub may replace obsolete PENDING +// members of those mutable-head groups; a replaced pending run is not a green verdict. Running +// cancellation is derived independently from fleet_compile_cancellation_standing: the live +// producer-absent standing yields NeverSupersedeRunning, so cancel-in-progress is literal false. +// merge_group and heal revalidation stay immutable and separately keyed (mg-{sha}, +// heal-{expected_healed_sha}); other dispatch uses run_id. data witness_floor_concurrency_group: String = "witness-floor-${{ github.event_name == 'merge_group' && format('mg-{0}', github.sha) || github.event_name == 'workflow_dispatch' && github.event.inputs.expected_healed_sha != '' && format('heal-{0}', github.event.inputs.expected_healed_sha) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'push' && github.ref || format('run-{0}', github.run_id) }}" // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. From 8b3dee168132b69bd3bd04c9bac790cf9d48d40c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 00:58:54 +0000 Subject: [PATCH 09/27] Derive the concurrency group from the event-name and heal-input authorities. The group string is concat of github_event_name_* and ci_heal_expected_healed_sha_input_name. The witness asserts those keys independently instead of grepping the same row's emission. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 50 +++++++++++++++++-- ...or_workflow_consolidation_witness_test.dag | 18 +++++-- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index eb3983396bd..f5652883a39 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -51,7 +51,9 @@ import extdeps.github.actions { RunnerSpec, checkout_action, setup_rust_action, github_event_name_pull_request, - github_event_name_push + github_event_name_push, + github_event_name_merge_group, + github_event_name_workflow_dispatch } import gunbc.ci_runner_target { gunbc_ci_selected_runner_spec } import gunbc.ci_heal_credential { @@ -226,7 +228,49 @@ data witness_floor_workflow_name: String = "witnesses" // producer-absent standing yields NeverSupersedeRunning, so cancel-in-progress is literal false. // merge_group and heal revalidation stay immutable and separately keyed (mg-{sha}, // heal-{expected_healed_sha}); other dispatch uses run_id. -data witness_floor_concurrency_group: String = "witness-floor-${{ github.event_name == 'merge_group' && format('mg-{0}', github.sha) || github.event_name == 'workflow_dispatch' && github.event.inputs.expected_healed_sha != '' && format('heal-{0}', github.event.inputs.expected_healed_sha) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'push' && github.ref || format('run-{0}', github.run_id) }}" +fn github_event_name_equals_expr(event: String) -> String { + concat(concat("github.event_name == '", event), "'") +} + +fn witness_floor_concurrency_group() -> String { + concat( + "witness-floor-${{ ", + concat( + github_event_name_equals_expr(event: github_event_name_merge_group), + concat( + " && format('mg-{0}', github.sha) || ", + concat( + github_event_name_equals_expr(event: github_event_name_workflow_dispatch), + concat( + " && github.event.inputs.", + concat( + ci_heal_expected_healed_sha_input_name, + concat( + " != '' && format('heal-{0}', github.event.inputs.", + concat( + ci_heal_expected_healed_sha_input_name, + concat( + ") || ", + concat( + github_event_name_equals_expr(event: github_event_name_pull_request), + concat( + " && format('pr-{0}', github.event.pull_request.number) || ", + concat( + github_event_name_equals_expr(event: github_event_name_push), + " && github.ref || format('run-{0}', github.run_id) }}" + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) +} // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // @@ -2689,7 +2733,7 @@ data witness_floor_workflow: Workflow = { concurrency: Present { value: concurrency_of_policy( policy: witness_floor_concurrency_policy, - group: witness_floor_concurrency_group) + group: witness_floor_concurrency_group()) }, env: Present { value: [ diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index bec72f67d74..dea6c38fbec 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, witness_floor_concurrency_group, mutable_head_runs_cancel_expression } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -11,7 +11,7 @@ import gunbc.fleet_converge_workflow { fleet_converge_workflow } import v2.workflow.bash_command_fold_serialize { bash_fold_serialize_program } import v2.std.diagnostic { Outcome } import extdeps.languages.yaml.gha_workflow { pr_activity_string } -import extdeps.github.actions { PullRequestActivity, Opened, Synchronize, Reopened, ReadyForReview, Closed } +import extdeps.github.actions { PullRequestActivity, Opened, Synchronize, Reopened, ReadyForReview, Closed, github_event_name_pull_request, github_event_name_push, github_event_name_merge_group, github_event_name_workflow_dispatch } import extdeps.github.actions { RunStep, UsesStep } // THE CI PHASES ARE THE PROGRAM'S TO SEQUENCE, NOT THE JOB'S (operator directive, 2026-08-20: @@ -470,10 +470,20 @@ test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subjec } } -test fn w_whole_workflow_concurrency_supersedes_mutable_heads_and_isolates_merge_group_and_heal() -> Bool { +test fn w_concurrency_group_keys_each_event_and_heal_input_and_refuses_running_cancel() -> Bool { match expected_witness_floor_yml() { WitnessFloorGenerated { content: yml } => - string_contains(s: yml, pattern: witness_floor_concurrency_group) + string_contains(s: yml, pattern: "format('mg-{0}', github.sha)") + && string_contains(s: yml, pattern: github_event_name_merge_group) + && string_contains(s: yml, pattern: github_event_name_workflow_dispatch) + && string_contains( + s: yml, + pattern: concat("format('heal-{0}', github.event.inputs.", ci_heal_expected_healed_sha_input_name) + ) + && string_contains(s: yml, pattern: github_event_name_pull_request) + && string_contains(s: yml, pattern: "format('pr-{0}', github.event.pull_request.number)") + && string_contains(s: yml, pattern: github_event_name_push) + && string_contains(s: yml, pattern: "github.ref || format('run-{0}', github.run_id)") && string_contains(s: yml, pattern: "cancel-in-progress: false") && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) && string_contains(s: yml, pattern: "timeout-minutes: 180") From b49128ba2e1560fcc05567ef6ed8e4207b445efe Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 01:12:36 +0000 Subject: [PATCH 10/27] Fold concurrency-group GHA from modeled event/subject rows and resolve fixtures. The group expression no longer embeds event-name literals; a fixture walk of the same branch list is the discriminating RED for swapped or dropped arms. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 151 ++++++++++++++---- ...or_workflow_consolidation_witness_test.dag | 75 ++++++--- 2 files changed, 173 insertions(+), 53 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index f5652883a39..726973981c7 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -16,7 +16,7 @@ import gunbc.workflow_capability_closure { workflow_job_capability_closure, capability_closure_is_closed, } -import v2.std.algebra { FreeSemigroup, list_append, list_map, non_empty_to_list } +import v2.std.algebra { FreeSemigroup, fold_list, list_append, list_map, non_empty_to_list } import extdeps.languages.yaml.emit { serialize_yaml } import extdeps.languages.yaml.gha_workflow { project_workflow_to_yaml } import v2.workflow.ci_workflow_run_emit { ci_toolchain_homes_isolated_or_refuse_command } @@ -232,46 +232,131 @@ fn github_event_name_equals_expr(event: String) -> String { concat(concat("github.event_name == '", event), "'") } +type WitnessFloorGroupBranch + = ShaAfterEvent { event: String, prefix: String } + | NonemptyInputAfterEvent { event: String, input: String, prefix: String } + | PrNumberAfterEvent { event: String, prefix: String } + | RefAfterEvent { event: String } + | RunIdFallback { prefix: String } + +fn gha_format_arg0(prefix: String, arg: String) -> String { + concat(concat("format('", prefix), concat("{0}', ", concat(arg, ")"))) +} + +fn gha_branch_predicate(branch: WitnessFloorGroupBranch) -> String { + match branch { + ShaAfterEvent { event: e, prefix: p } => + concat(concat(github_event_name_equals_expr(event: e), " && "), gha_format_arg0(prefix: p, arg: "github.sha")) + NonemptyInputAfterEvent { event: e, input: i, prefix: p } => + concat( + concat(github_event_name_equals_expr(event: e), concat(" && github.event.inputs.", concat(i, " != '' && "))), + gha_format_arg0(prefix: p, arg: concat("github.event.inputs.", i)) + ) + PrNumberAfterEvent { event: e, prefix: p } => + concat(concat(github_event_name_equals_expr(event: e), " && "), gha_format_arg0(prefix: p, arg: "github.event.pull_request.number")) + RefAfterEvent { event: e } => + concat(github_event_name_equals_expr(event: e), " && github.ref") + RunIdFallback { prefix: p } => + gha_format_arg0(prefix: p, arg: "github.run_id") + } +} + +fn gha_or_join(acc: String, next: String) -> String { + if acc == "" { + next + } else { + concat(concat(acc, " || "), next) + } +} + +fn witness_floor_group_branches() -> List { + [ + ShaAfterEvent { event: github_event_name_merge_group, prefix: "mg-" }, + NonemptyInputAfterEvent { + event: github_event_name_workflow_dispatch, + input: ci_heal_expected_healed_sha_input_name, + prefix: "heal-" + }, + PrNumberAfterEvent { event: github_event_name_pull_request, prefix: "pr-" }, + RefAfterEvent { event: github_event_name_push }, + RunIdFallback { prefix: "run-" } + ] +} + fn witness_floor_concurrency_group() -> String { concat( "witness-floor-${{ ", concat( - github_event_name_equals_expr(event: github_event_name_merge_group), - concat( - " && format('mg-{0}', github.sha) || ", - concat( - github_event_name_equals_expr(event: github_event_name_workflow_dispatch), - concat( - " && github.event.inputs.", - concat( - ci_heal_expected_healed_sha_input_name, - concat( - " != '' && format('heal-{0}', github.event.inputs.", - concat( - ci_heal_expected_healed_sha_input_name, - concat( - ") || ", - concat( - github_event_name_equals_expr(event: github_event_name_pull_request), - concat( - " && format('pr-{0}', github.event.pull_request.number) || ", - concat( - github_event_name_equals_expr(event: github_event_name_push), - " && github.ref || format('run-{0}', github.run_id) }}" - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) + fold_list( + xs: witness_floor_group_branches(), + empty: "", + cons: fn(acc, b) { gha_or_join(acc: acc, next: gha_branch_predicate(branch: b)) } + ), + " }}" ) ) } +type WitnessFloorGroupFixture { + event: String + sha: String + run_id: String + pull_request_number: String + expected_healed_sha: String + github_ref: String +} + +type ChosenGroupSuffix + = UnchosenGroupSuffix + | ChosenGroupSuffix { value: String } + +fn group_branch_matches(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> Bool { + match branch { + ShaAfterEvent { event: e, prefix: _ } => fx.event == e + NonemptyInputAfterEvent { event: e, input: _, prefix: _ } => fx.event == e && fx.expected_healed_sha != "" + PrNumberAfterEvent { event: e, prefix: _ } => fx.event == e + RefAfterEvent { event: e } => fx.event == e + RunIdFallback { prefix: _ } => true + } +} + +fn group_branch_suffix(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> String { + match branch { + ShaAfterEvent { event: _, prefix: p } => concat(p, fx.sha) + NonemptyInputAfterEvent { event: _, input: _, prefix: p } => concat(p, fx.expected_healed_sha) + PrNumberAfterEvent { event: _, prefix: p } => concat(p, fx.pull_request_number) + RefAfterEvent { event: _ } => fx.github_ref + RunIdFallback { prefix: p } => concat(p, fx.run_id) + } +} + +fn choose_group_suffix(acc: ChosenGroupSuffix, branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> ChosenGroupSuffix { + match acc { + ChosenGroupSuffix { value: v } => ChosenGroupSuffix { value: v } + UnchosenGroupSuffix => + if group_branch_matches(branch: branch, fx: fx) { + ChosenGroupSuffix { value: group_branch_suffix(branch: branch, fx: fx) } + } else { + UnchosenGroupSuffix + } + } +} + +fn resolve_witness_floor_group_suffix(fx: WitnessFloorGroupFixture) -> String { + match fold_list( + xs: witness_floor_group_branches(), + empty: UnchosenGroupSuffix, + cons: fn(acc, b) { choose_group_suffix(acc: acc, branch: b, fx: fx) } + ) { + ChosenGroupSuffix { value: v } => v + UnchosenGroupSuffix => "" + } +} + +fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> String { + concat("witness-floor-", resolve_witness_floor_group_suffix(fx: fx)) +} + // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // // This value decides whether GitHub cancels an in-flight run when a newer one joins the group. It diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index dea6c38fbec..0c20d71c5d1 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, WitnessFloorGroupFixture, resolve_witness_floor_concurrency_group } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -470,24 +470,59 @@ test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subjec } } -test fn w_concurrency_group_keys_each_event_and_heal_input_and_refuses_running_cancel() -> Bool { - match expected_witness_floor_yml() { - WitnessFloorGenerated { content: yml } => - string_contains(s: yml, pattern: "format('mg-{0}', github.sha)") - && string_contains(s: yml, pattern: github_event_name_merge_group) - && string_contains(s: yml, pattern: github_event_name_workflow_dispatch) - && string_contains( - s: yml, - pattern: concat("format('heal-{0}', github.event.inputs.", ci_heal_expected_healed_sha_input_name) - ) - && string_contains(s: yml, pattern: github_event_name_pull_request) - && string_contains(s: yml, pattern: "format('pr-{0}', github.event.pull_request.number)") - && string_contains(s: yml, pattern: github_event_name_push) - && string_contains(s: yml, pattern: "github.ref || format('run-{0}', github.run_id)") - && string_contains(s: yml, pattern: "cancel-in-progress: false") - && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) - && string_contains(s: yml, pattern: "timeout-minutes: 180") - && !string_contains(s: yml, pattern: "timeout-minutes: 360") - WitnessFloorGenerationRefused { reason: _ } => false +test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_cancel() -> Bool { + let mg = WitnessFloorGroupFixture { + event: github_event_name_merge_group, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + expected_healed_sha: "", + github_ref: "refs/heads/main" + } + let heal = WitnessFloorGroupFixture { + event: github_event_name_workflow_dispatch, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + expected_healed_sha: "healsha", + github_ref: "refs/heads/main" + } + let bare_dispatch = WitnessFloorGroupFixture { + event: github_event_name_workflow_dispatch, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + expected_healed_sha: "", + github_ref: "refs/heads/main" + } + let pr = WitnessFloorGroupFixture { + event: github_event_name_pull_request, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + expected_healed_sha: "", + github_ref: "refs/heads/main" + } + let push = WitnessFloorGroupFixture { + event: github_event_name_push, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + expected_healed_sha: "", + github_ref: "refs/heads/main" } + resolve_witness_floor_concurrency_group(fx: mg) == "witness-floor-mg-mgsha" + && resolve_witness_floor_concurrency_group(fx: heal) == "witness-floor-heal-healsha" + && resolve_witness_floor_concurrency_group(fx: bare_dispatch) == "witness-floor-run-rid" + && resolve_witness_floor_concurrency_group(fx: pr) == "witness-floor-pr-7" + && resolve_witness_floor_concurrency_group(fx: push) == "witness-floor-refs/heads/main" + && resolve_witness_floor_concurrency_group(fx: push) != "witness-floor-pr-7" + && match expected_witness_floor_yml() { + WitnessFloorGenerated { content: yml } => + string_contains(s: yml, pattern: "cancel-in-progress: false") + && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) + && string_contains(s: yml, pattern: "timeout-minutes: 180") + && !string_contains(s: yml, pattern: "timeout-minutes: 360") + WitnessFloorGenerationRefused { reason: _ } => false + } } From 6b088db64a896f94e06b7bf72a6b7cc02f9be889 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 01:14:16 +0000 Subject: [PATCH 11/27] Require joined event-and-subject group patterns and a crossed-pairing RED. Independent fragment checks stay green if pull_request and push subjects swap; one concat per branch plus an absent push+pr pairing makes that swap authorable. Co-authored-by: Cursor --- ...or_workflow_consolidation_witness_test.dag | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 0c20d71c5d1..6a94f483fdb 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, WitnessFloorGroupFixture, resolve_witness_floor_concurrency_group } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, github_event_name_equals_expr, WitnessFloorGroupFixture, resolve_witness_floor_concurrency_group } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -519,7 +519,36 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc && resolve_witness_floor_concurrency_group(fx: push) != "witness-floor-pr-7" && match expected_witness_floor_yml() { WitnessFloorGenerated { content: yml } => - string_contains(s: yml, pattern: "cancel-in-progress: false") + string_contains( + s: yml, + pattern: concat(github_event_name_equals_expr(event: github_event_name_merge_group), " && format('mg-{0}', github.sha)") + ) + && string_contains( + s: yml, + pattern: concat( + github_event_name_equals_expr(event: github_event_name_workflow_dispatch), + concat( + " && github.event.inputs.", + concat( + ci_heal_expected_healed_sha_input_name, + concat(" != '' && format('heal-{0}', github.event.inputs.", concat(ci_heal_expected_healed_sha_input_name, ")")) + ) + ) + ) + ) + && string_contains( + s: yml, + pattern: concat(github_event_name_equals_expr(event: github_event_name_pull_request), " && format('pr-{0}', github.event.pull_request.number)") + ) + && string_contains( + s: yml, + pattern: concat(github_event_name_equals_expr(event: github_event_name_push), " && github.ref") + ) + && !string_contains( + s: yml, + pattern: concat(github_event_name_equals_expr(event: github_event_name_push), " && format('pr-{0}'") + ) + && string_contains(s: yml, pattern: "cancel-in-progress: false") && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) && string_contains(s: yml, pattern: "timeout-minutes: 180") && !string_contains(s: yml, pattern: "timeout-minutes: 360") From 11345f927f493a3e8bf933f34738fe5d645daefd Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 01:45:50 +0000 Subject: [PATCH 12/27] Declare the main per-commit floor drop and bind the heal resolver to the input row. Occupancy grouping lowers a stated unique-run_id guarantee, so the 4b(3) row lands with this change; citations and the fixture lookup follow the same head. Co-authored-by: Cursor --- dag/gunbc/doc_graph_roots.dag | 2 +- dag/gunbc/plans/ci_process_end_to_end.dag | 2 +- .../main_per_commit_floor_verdict.dag | 34 +++++++++++++++++++ dag/gunbc/rung_drop/roster.dag | 2 ++ dag/gunbc/witness/witness_floor_workflow.dag | 31 +++++++++++++++-- ...or_workflow_consolidation_witness_test.dag | 24 +++++++------ docs/design-rung-drops.md | 4 +++ 7 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag diff --git a/dag/gunbc/doc_graph_roots.dag b/dag/gunbc/doc_graph_roots.dag index 3e1062237bd..311cd125f7a 100644 --- a/dag/gunbc/doc_graph_roots.dag +++ b/dag/gunbc/doc_graph_roots.dag @@ -1614,7 +1614,7 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [ DeclarationRef { module_path: "gunbc.witness_floor_workflow", decl_name: "witness_floor_concurrency_group", field: WholeDeclaration }, ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the six findings this note records outlive their archived lanes only until each has a carrier of its own: the rejected_with_pending diagnostic-order defect, the entry-vs-member position divergence, the three-entrypoint admission divergence, the standing section 4c population that makes a full-corpus compile exit 1, the floor-gated deploy, and the run_id concurrency key -- that last one now carried by gunbc.witness_floor_workflow witness_floor_concurrency_group, which states the run_id fallback as the 2026-08-17 operator ruling that every push to main must finish, and by witness_floor_concurrency_policy, which derives the cancel axis from the observed fleet standing; the note is repointed there because the ci.yml-era second authority it used to cite was deleted with its workflow. When each is either fixed or carried as a typed row, the note is a historical account and deletes with this bind. It is registered rather than deleted today because four sessions were archived holding these measurements and nothing else records them — but a cross-lane status narrative is the parallel-ledger shape DESIGN section 6 warns about, so its home is a question for the operator, not a settled one.") }, + dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the six findings this note records outlive their archived lanes only until each has a carrier of its own: the rejected_with_pending diagnostic-order defect, the entry-vs-member position divergence, the three-entrypoint admission divergence, the standing section 4c population that makes a full-corpus compile exit 1, the floor-gated deploy, and the concurrency-group key -- that last one now carried by gunbc.witness_floor_workflow witness_floor_concurrency_group, which keys merge_group by sha, heal dispatch by expected_healed_sha, pull_request by number, push by github.ref, and run_id only as the bare-dispatch fallback, with the 2026-08-17 every-main-must-finish unique-run_id reading retired as gunbc.rung_drop main_per_commit_floor_verdict, and by witness_floor_concurrency_policy, which derives the cancel axis from the observed fleet standing; the note is repointed there because the ci.yml-era second authority it used to cite was deleted with its workflow. When each is either fixed or carried as a typed row, the note is a historical account and deletes with this bind. It is registered rather than deleted today because four sessions were archived holding these measurements and nothing else records them — but a cross-lane status narrative is the parallel-ledger shape DESIGN section 6 warns about, so its home is a question for the operator, not a settled one.") }, }, HandAuthoredDocBind { home: PlanDoc, diff --git a/dag/gunbc/plans/ci_process_end_to_end.dag b/dag/gunbc/plans/ci_process_end_to_end.dag index 457de86aa91..0c724b22e4b 100644 --- a/dag/gunbc/plans/ci_process_end_to_end.dag +++ b/dag/gunbc/plans/ci_process_end_to_end.dag @@ -28,7 +28,7 @@ fn ci_process_end_to_end_body() -> List { rows: [ row(cells: [cell(text: "1"), cell(text: "push branch / open-sync PR"), cell(text: "🔴"), cell(text: "—"), cell(text: "git + GitHub native")]), row(cells: [cell(text: "2"), cell(text: "GitHub reads committed `ci.yml` triggers, starts run"), cell(text: "🟢 *file* / 🔴 *act*"), cell(text: "~0"), cell(text: "file derived+gated; dispatch engine native. **Bootstrap seam**: reads the *committed* file → stale-on-invocation-surface is uncaught")]), - row(cells: [cell(text: "3"), cell(text: "concurrency group + cancel-in-progress"), cell(text: "🟢 *key* / 🔴 *eval*"), cell(text: "~0"), cell(text: "key modeled (`witness_floor_concurrency_group`); the `run_id` fallback this row read as a dup bug is the declared main-coverage ruling, not a defect; evaluated by GitHub")]), + row(cells: [cell(text: "3"), cell(text: "concurrency group + cancel-in-progress"), cell(text: "🟢 *key* / 🔴 *eval*"), cell(text: "~0"), cell(text: "key modeled (`witness_floor_concurrency_group`); run_id is the bare-dispatch fallback only; per-main unique-run_id coverage is the declared drop `gunbc.rung_drop` `main_per_commit_floor_verdict`; cancel is `witness_floor_concurrency_policy`; evaluated by GitHub")]), row(cells: [cell(text: "3′"), cell(text: "dashboard *also* fires `workflow_dispatch` same SHA"), cell(text: "🔴"), cell(text: "—"), cell(text: "ctrl/dashboard — **the dup source**")]), row(cells: [cell(text: "4"), cell(text: "GitHub matches labels & **places the job on a host**"), cell(text: "🟢 *labels* / 🔴 *placement*"), cell(text: "~0 (queue)"), cell(text: "labels derived (`runner_spec_from_offer`); **placement is native, demand-blind, first-idle** ← **underutilization root (G1)**")]), row(cells: [cell(text: "5"), cell(text: "a runner daemon on srv1/srv2 picks it up"), cell(text: "🔴 (🟡 inventory)"), cell(text: "—"), cell(text: "runners/host, registration, on-box labels = **hand-run shell, no repo artifact (G2)**; `operator_fleet` only *describes* the hosts")]), diff --git a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag new file mode 100644 index 00000000000..2cfb37ea36f --- /dev/null +++ b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag @@ -0,0 +1,34 @@ +module gunbc.rung_drop.main_per_commit_floor_verdict + +import std.types { NonEmptyStr } +import gunbc.rung_drop { RungDrop, Standing, TypedDeclaration, ReplacementStaged } +import gunbc.guarantee_rung { MechanicallyPreventable, Mitigatable } + +// DECLARED 2026-09-11 with P0a.1 occupancy grouping (gunbc#10981, review 63340). +// The 2026-08-17 unique-run_id grouping stated that every push to main must finish, because sharing +// one main group drops queued observations. The 2026-09-10 occupancy ruling replaces that key with +// github.ref for push events, so GitHub may replace pending members of the main group. Running +// cancel stays NeverSupersedeRunning. DESIGN 4b(3) requires the drop row beside the change that +// lowers the rung. + +data main_per_commit_floor_verdict: RungDrop = RungDrop { + identity: "main_per_commit_floor_verdict" as NonEmptyStr, + + subject: "every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group", + + declared: "2026-09-11", + + standing: Standing, + + declaration: TypedDeclaration { + previous: MechanicallyPreventable, + temporary: Mitigatable, + reason: ReplacementStaged { + replacement: "witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false" + }, + population: [ + "push events on refs/heads/main" + ], + restoration_trigger: "EITHER (a) fleet cancellation safety is observed -- running_supersession_for_fleet_standing yields SupersedeMutableHeadRuns, so each main SHA's run starts promptly and supersedes only running work -- OR (b) typed affected-set admission is live (P0a.2) so a per-SHA verdict is cheap enough to restore unique-run_id grouping for main. SUFFICIENT FOR: unique-run_id grouping restored on refs/heads/main so every main SHA obtains its own started floor/build/native verdict (per-commit attribution and bisection grain). Neither arm is retired by an artifact: re-adding run_id to the group string without (a) or (b) re-imports occupancy cost; observing fleet-safe running cancel without restoring unique grouping does not restore per-commit attribution. A superseded pending run is not a green verdict and does not discharge this row" + } +} diff --git a/dag/gunbc/rung_drop/roster.dag b/dag/gunbc/rung_drop/roster.dag index 160a8bd01fc..525e4f62c3c 100644 --- a/dag/gunbc/rung_drop/roster.dag +++ b/dag/gunbc/rung_drop/roster.dag @@ -59,6 +59,7 @@ import gunbc.rung_drop.deleted_cadence_reference_drop { deleted_cadence_referenc import gunbc.rung_drop.floor_cost_debt_straddling_produced_decl { floor_cost_debt_straddling_produced_decl } import gunbc.rung_drop.determinism_transitive_reachability { determinism_transitive_reachability } import gunbc.rung_drop.transitional_admission_exception { transitional_admission_exception } +import gunbc.rung_drop.main_per_commit_floor_verdict { main_per_commit_floor_verdict } data rung_drop_roster: List = [ floor_cut_heal, @@ -101,6 +102,7 @@ data rung_drop_roster: List = [ floor_cost_debt_straddling_produced_decl, determinism_transitive_reachability, transitional_admission_exception(), + main_per_commit_floor_verdict, ] // THE DERIVATION THE PROJECTION USES, so "standing today" has one authority and not two. The diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 726973981c7..89a4f8b20c5 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -297,23 +297,47 @@ fn witness_floor_concurrency_group() -> String { ) } +type DispatchInputBinding { + name: String + value: String +} + type WitnessFloorGroupFixture { event: String sha: String run_id: String pull_request_number: String - expected_healed_sha: String github_ref: String + dispatch_inputs: List } type ChosenGroupSuffix = UnchosenGroupSuffix | ChosenGroupSuffix { value: String } +fn dispatch_input_value(bindings: List, name: String) -> String { + fold_list( + xs: bindings, + empty: "", + cons: fn(acc, b) { + if acc != "" { + acc + } else { + if b.name == name { + b.value + } else { + "" + } + } + } + ) +} + fn group_branch_matches(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> Bool { match branch { ShaAfterEvent { event: e, prefix: _ } => fx.event == e - NonemptyInputAfterEvent { event: e, input: _, prefix: _ } => fx.event == e && fx.expected_healed_sha != "" + NonemptyInputAfterEvent { event: e, input: i, prefix: _ } => + fx.event == e && dispatch_input_value(bindings: fx.dispatch_inputs, name: i) != "" PrNumberAfterEvent { event: e, prefix: _ } => fx.event == e RefAfterEvent { event: e } => fx.event == e RunIdFallback { prefix: _ } => true @@ -323,7 +347,8 @@ fn group_branch_matches(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFi fn group_branch_suffix(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> String { match branch { ShaAfterEvent { event: _, prefix: p } => concat(p, fx.sha) - NonemptyInputAfterEvent { event: _, input: _, prefix: p } => concat(p, fx.expected_healed_sha) + NonemptyInputAfterEvent { event: _, input: i, prefix: p } => + concat(p, dispatch_input_value(bindings: fx.dispatch_inputs, name: i)) PrNumberAfterEvent { event: _, prefix: p } => concat(p, fx.pull_request_number) RefAfterEvent { event: _ } => fx.github_ref RunIdFallback { prefix: p } => concat(p, fx.run_id) diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 6a94f483fdb..7850b731866 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, github_event_name_equals_expr, WitnessFloorGroupFixture, resolve_witness_floor_concurrency_group } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, github_event_name_equals_expr, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group } import gunbc.witness_v2_native_route { required_v2_native_job_name } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } @@ -476,40 +476,42 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc sha: "mgsha", run_id: "rid", pull_request_number: "7", - expected_healed_sha: "", - github_ref: "refs/heads/main" + github_ref: "refs/heads/main", + dispatch_inputs: [] } let heal = WitnessFloorGroupFixture { event: github_event_name_workflow_dispatch, sha: "mgsha", run_id: "rid", pull_request_number: "7", - expected_healed_sha: "healsha", - github_ref: "refs/heads/main" + github_ref: "refs/heads/main", + dispatch_inputs: [ + DispatchInputBinding { name: ci_heal_expected_healed_sha_input_name, value: "healsha" } + ] } let bare_dispatch = WitnessFloorGroupFixture { event: github_event_name_workflow_dispatch, sha: "mgsha", run_id: "rid", pull_request_number: "7", - expected_healed_sha: "", - github_ref: "refs/heads/main" + github_ref: "refs/heads/main", + dispatch_inputs: [] } let pr = WitnessFloorGroupFixture { event: github_event_name_pull_request, sha: "mgsha", run_id: "rid", pull_request_number: "7", - expected_healed_sha: "", - github_ref: "refs/heads/main" + github_ref: "refs/heads/main", + dispatch_inputs: [] } let push = WitnessFloorGroupFixture { event: github_event_name_push, sha: "mgsha", run_id: "rid", pull_request_number: "7", - expected_healed_sha: "", - github_ref: "refs/heads/main" + github_ref: "refs/heads/main", + dispatch_inputs: [] } resolve_witness_floor_concurrency_group(fx: mg) == "witness-floor-mg-mgsha" && resolve_witness_floor_concurrency_group(fx: heal) == "witness-floor-heal-healsha" diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 987488b876e..0e780b913c1 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -205,3 +205,7 @@ Transitive determinism reachability over the fn-arrow call graph: RUNG DROP, mec ### admission rows on falsifier-family cadences with no scheduled route, awaiting transition to a cadence that executes — declared 2026-09-08 admission rows on falsifier-family cadences with no scheduled route, awaiting transition to a cadence that executes: RUNG DROP, mitigatable -> outside the ladder (lost as a passenger of .github/workflows/falsifier.yml, deleted at 611fd02770 (#8283)). Population: dag/test/claim/method_arg_declared_contract_witness_test.dag::w_method_arg_infers_against_declared_contract_not_element_type_test, src/v2/test/claim/manual/english_emit_add_test.dag::english_emit_add_ingest_round_trip_holds, dag/test/claim/legacy_test_behavior_disposition_acceptance_test.dag::legacy_test_behavior_unclassified_frontier_is_zero, dag/test/claim/observation_raw_print_retirement_acceptance_test.dag::observation_emit_frontier_is_zero, dag/test/claim/long/g2_data_reference_under_selection_witness_test.dag::specimen_classifies_as_runtime_read, dag/test/claim/long/g2_data_reference_under_selection_witness_test.dag::specimen_does_not_classify_as_local_read, dag/test/claim/source_integration_proof_kernel_acceptance_test.dag::witness_p1_proof_kernel_acceptance_contract_holds, dag/test/claim/namespace_reference_derived_closure_acceptance_test.dag::witness_namespace_reference_derived_closure_closing_contract_holds, src/v2/test/claim/long/direct_rust_door_production_group_test.dag::direct_rust_door_production_group_closing_expectation_holds, dag/test/claim/self_host_parse_engine_hooks_behavioral_witness_test.dag::self_host_parse_engine_hooks_behavioral_receipt_holds, dag/test/claim/self_host_use_site_verdict_behavioral_witness_test.dag::self_host_use_site_verdict_behavioral_receipt_holds, dag/test/claim/self_host_body_producer_behavioral_witness_test.dag::self_host_body_producer_behavioral_receipt_holds, dag/test/claim/self_host_discovery_enumeration_behavioral_witness_test.dag::self_host_discovery_enumeration_behavioral_receipt_holds, dag/test/claim/self_host_target_carriers_behavioral_witness_test.dag::self_host_target_carriers_behavioral_receipt_holds, dag/test/claim/self_host_03_normalize_behavioral_witness_test.dag::self_host_03_normalize_behavioral_receipt_holds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_live_scope_fixture_origin_discoverable_in_entry_closure_reds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_live_structural_fixture_mint_site_discovered_reds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_ingest_mint_surface_requires_call_occurrence_frontier_reds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_direct_door_smoke_requires_closure_or_call_occurrence_frontier_reds, src/v2/test/claim/self_host/compiler_closure_emit_from_ingest_test.dag::compiler_closure_scoped_ingest_module_count_ok_holds, src/v2/test/claim/self_host/compiler_closure_emit_from_ingest_test.dag::compiler_closure_ingest_overflow_refuses_never_empty_holds, src/v2/test/claim/self_host/compiler_closure_ingest_boundary_witness_test.dag::compiler_closure_ingest_boundary_exact_cap_transport_holds, src/v2/test/claim/long/infer_transform_binary_infix_witness_test.dag::infer_transform_add_vertical_witness_holds. Restored when: each named identity carries a cadence with a live scheduled route, at which point each row above is removed from transitional_admission_exception_identities and the identity's exception in explicit_witness_admission.dag is deleted (the gate consumes the list, so removing from the list retires the exception). The cadence category those routes need is gunbc.rung_drop.shared_capability missing_cadence_category; the remainder is the per-identity migration onto a live route. Waiters whose remaining unsatisfied condition is only that capability retire together. When the category fires and any named identity still lacks a live scheduled route, this identity is removed from missing_cadence_category.waiter_identities in the SAME change as those retirements (delist, not SplitRetirement) and this row stays Standing until every named identity has that route. If every named identity already has a live scheduled route when the category fires, retire with the enrolled set.. + +### every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group — declared 2026-09-11 + +every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: EITHER (a) fleet cancellation safety is observed -- running_supersession_for_fleet_standing yields SupersedeMutableHeadRuns, so each main SHA's run starts promptly and supersedes only running work -- OR (b) typed affected-set admission is live (P0a.2) so a per-SHA verdict is cheap enough to restore unique-run_id grouping for main. SUFFICIENT FOR: unique-run_id grouping restored on refs/heads/main so every main SHA obtains its own started floor/build/native verdict (per-commit attribution and bisection grain). Neither arm is retired by an artifact: re-adding run_id to the group string without (a) or (b) re-imports occupancy cost; observing fleet-safe running cancel without restoring unique grouping does not restore per-commit attribution. A superseded pending run is not a green verdict and does not discharge this row. From ba68bd56c94064c8dd7fbd2bed3a3db9b302a8f2 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 02:07:59 +0000 Subject: [PATCH 13/27] Correct the main-floor drop trigger and unbind timeout from the grouping witness. The lost property is SHA-bound adjudication of superseded pending mains, not fleet-safe SIGKILL or P0a.2 as currently specified; timeout is a different envelope and must not live in this witness. Co-authored-by: Cursor --- dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag | 2 +- .../claim/witness_floor_workflow_consolidation_witness_test.dag | 2 -- docs/design-rung-drops.md | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag index 2cfb37ea36f..babc18eb9ab 100644 --- a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag +++ b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag @@ -29,6 +29,6 @@ data main_per_commit_floor_verdict: RungDrop = RungDrop { population: [ "push events on refs/heads/main" ], - restoration_trigger: "EITHER (a) fleet cancellation safety is observed -- running_supersession_for_fleet_standing yields SupersedeMutableHeadRuns, so each main SHA's run starts promptly and supersedes only running work -- OR (b) typed affected-set admission is live (P0a.2) so a per-SHA verdict is cheap enough to restore unique-run_id grouping for main. SUFFICIENT FOR: unique-run_id grouping restored on refs/heads/main so every main SHA obtains its own started floor/build/native verdict (per-commit attribution and bisection grain). Neither arm is retired by an artifact: re-adding run_id to the group string without (a) or (b) re-imports occupancy cost; observing fleet-safe running cancel without restoring unique grouping does not restore per-commit attribution. A superseded pending run is not a green verdict and does not discharge this row" + restoration_trigger: "every superseded pending refs/heads/main SHA receives a terminal adjudication bound to that SHA -- either its affected native subjects execute and pass, or non-affection/coverage by a superseding execution is proved for that SHA. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. P0a.2 is the expected route only if and when it is wired to adjudicate skipped main heads; landing typed affected-set admission that only judges subjects inside a workflow that already executed does not retire this row. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row" } } diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 7850b731866..f244dd462d5 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -552,8 +552,6 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc ) && string_contains(s: yml, pattern: "cancel-in-progress: false") && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) - && string_contains(s: yml, pattern: "timeout-minutes: 180") - && !string_contains(s: yml, pattern: "timeout-minutes: 360") WitnessFloorGenerationRefused { reason: _ } => false } } diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 0e780b913c1..98a642739da 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -208,4 +208,4 @@ admission rows on falsifier-family cadences with no scheduled route, awaiting tr ### every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group — declared 2026-09-11 -every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: EITHER (a) fleet cancellation safety is observed -- running_supersession_for_fleet_standing yields SupersedeMutableHeadRuns, so each main SHA's run starts promptly and supersedes only running work -- OR (b) typed affected-set admission is live (P0a.2) so a per-SHA verdict is cheap enough to restore unique-run_id grouping for main. SUFFICIENT FOR: unique-run_id grouping restored on refs/heads/main so every main SHA obtains its own started floor/build/native verdict (per-commit attribution and bisection grain). Neither arm is retired by an artifact: re-adding run_id to the group string without (a) or (b) re-imports occupancy cost; observing fleet-safe running cancel without restoring unique grouping does not restore per-commit attribution. A superseded pending run is not a green verdict and does not discharge this row. +every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: every superseded pending refs/heads/main SHA receives a terminal adjudication bound to that SHA -- either its affected native subjects execute and pass, or non-affection/coverage by a superseding execution is proved for that SHA. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. P0a.2 is the expected route only if and when it is wired to adjudicate skipped main heads; landing typed affected-set admission that only judges subjects inside a workflow that already executed does not retire this row. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row. From 9a9adc25713369238c41a3f287be2bf042057e01 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Fri, 11 Sep 2026 02:10:10 +0000 Subject: [PATCH 14/27] Delete the required-v2-native CI job; the route stays as an operator-invoked instrument Operator ruling 2026-09-11. The job landed with #10882 citing a ~70-minute native run inside a 180-minute envelope; the only full run on the real runner class took 4h06m at 14.8 GB, and the workflow's concurrency group (per PR number or run id, cancel-in-progress false) never supersedes a running job, so every push added another ~4h claimant. 31 native jobs were running concurrently this morning and the build and floor lanes were failing on main under memory stalls and budget interruptions. Cut at the root: WitnessFloorV2NativeLane leaves gunbc.witness_floor_workflow, the v2-native lane and phase leave gunbc.required_ci_phase_roster and the host binary, witnesses.yml and docs/design-rung-drops.md are regenerated. The route model, harness and admission authority are unchanged and reachable as claim_executor --v2-native-route. Declared as the 4b(3) drop gunbc.rung_drop v2_native_route_off_the_merge_path with a capability-shaped trigger; the consolidation witness reds if required-v2-native returns. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DQyBe1FaYb3bFZmpTczetX --- .github/workflows/witnesses.yml | 95 +------------- dag/gunbc/fabric/fabric_witness_run.dag | 11 +- dag/gunbc/required_ci_phase_roster.dag | 11 +- dag/gunbc/rung_drop/roster.dag | 2 + .../v2_native_route_off_the_merge_path.dag | 54 ++++++++ dag/gunbc/witness/witness_floor_workflow.dag | 124 ++---------------- ...or_workflow_consolidation_witness_test.dag | 32 ++--- docs/design-rung-drops.md | 4 + src/v1/stage0/src/bin/claim_executor.rs | 116 ++++++++-------- .../src/cli_run/required_lane_roster.rs | 9 +- 10 files changed, 160 insertions(+), 298 deletions(-) create mode 100644 dag/gunbc/rung_drop/v2_native_route_off_the_merge_path.dag diff --git a/.github/workflows/witnesses.yml b/.github/workflows/witnesses.yml index e0b45318b6f..552145a635a 100644 --- a/.github/workflows/witnesses.yml +++ b/.github/workflows/witnesses.yml @@ -403,91 +403,9 @@ jobs: retention-days: 14 include-hidden-files: true if: always() && steps.heal_commit_push.outputs.heal_author_commit_required == '1' - required-v2-native: - runs-on: [self-hosted, linux, arm64] - timeout-minutes: 180 - steps: - - name: Record runner filesystem at job start - run: | - # dissolve-on: toolchain_filesystem_probe -- delete the start/end runner-filesystem instrument after its joined readings identify and the fleet fixes the toolchain deleter, OR after per-job runner microVMs make the shared filesystem eviction class impossible - GUNBC_TOOLCHAIN_FS_PREFIX='GUNBC_TOOLCHAIN_FS phase=start' - echo "$GUNBC_TOOLCHAIN_FS_PREFIX time_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo unavailable) host=$(hostname 2>/dev/null || echo unavailable) runner=${RUNNER_NAME:-unset}" - echo "$GUNBC_TOOLCHAIN_FS_PREFIX runner_temp=${RUNNER_TEMP:-unset} rustup_home=${RUSTUP_HOME:-unset} cargo_home=${CARGO_HOME:-unset} uptime_seconds=$(cut -d' ' -f1 /proc/uptime 2>/dev/null || echo unavailable)" - echo "$GUNBC_TOOLCHAIN_FS_PREFIX loadavg=$(tr ' ' ',' < /proc/loadavg 2>/dev/null || echo unavailable)" - df -Pk "${RUNNER_TEMP:-/path-that-does-not-exist}" 2>&1 | while IFS= read -r GUNBC_TOOLCHAIN_FS_LINE; do echo "$GUNBC_TOOLCHAIN_FS_PREFIX df_blocks=$GUNBC_TOOLCHAIN_FS_LINE"; done - df -Pik "${RUNNER_TEMP:-/path-that-does-not-exist}" 2>&1 | while IFS= read -r GUNBC_TOOLCHAIN_FS_LINE; do echo "$GUNBC_TOOLCHAIN_FS_PREFIX df_inodes=$GUNBC_TOOLCHAIN_FS_LINE"; done - for GUNBC_TOOLCHAIN_FS_PATH in "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains/*/bin/rustc "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains/*/bin/rustfmt "${RUNNER_TEMP:-/path-that-does-not-exist}"/cargo/bin/cargo; do if [ -e "$GUNBC_TOOLCHAIN_FS_PATH" ]; then GUNBC_TOOLCHAIN_FS_STAT=$(stat -c 'size=%s mtime_epoch=%Y mtime=%y inode=%i' "$GUNBC_TOOLCHAIN_FS_PATH" 2>&1 || echo stat-unavailable); echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=file path=$GUNBC_TOOLCHAIN_FS_PATH exists=yes $GUNBC_TOOLCHAIN_FS_STAT"; else echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=file path=$GUNBC_TOOLCHAIN_FS_PATH exists=no size=unavailable mtime_epoch=unavailable mtime=unavailable inode=unavailable"; fi; done - for GUNBC_TOOLCHAIN_FS_PATH in "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains/*/bin "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains "${RUNNER_TEMP:-/path-that-does-not-exist}"/cargo/bin; do if [ -d "$GUNBC_TOOLCHAIN_FS_PATH" ]; then GUNBC_TOOLCHAIN_FS_STAT=$(stat -c 'mtime_epoch=%Y mtime=%y inode=%i' "$GUNBC_TOOLCHAIN_FS_PATH" 2>&1 || echo stat-unavailable); echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=directory path=$GUNBC_TOOLCHAIN_FS_PATH exists=yes $GUNBC_TOOLCHAIN_FS_STAT"; else echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=directory path=$GUNBC_TOOLCHAIN_FS_PATH exists=no mtime_epoch=unavailable mtime=unavailable inode=unavailable"; fi; done - if: always() - - name: Checkout - uses: actions/checkout@v5 - with: - fetch-depth: 0 - ref: ${{ inputs.expected_healed_sha || github.sha }} - - name: Refuse a heal revalidation whose run subject or checkout does not name the expected healed SHA - run: | - EXPECTED_HEALED_SHA="${{ inputs.expected_healed_sha }}" - RUN_SUBJECT_HEAD="${{ github.sha }}" - if [ -n "$EXPECTED_HEALED_SHA" ]; then - ACTUAL_HEAD="$(git rev-parse HEAD)" - if ! [ "$RUN_SUBJECT_HEAD" = "$EXPECTED_HEALED_SHA" ]; then - echo "::error::heal revalidation refused: workflow run subject $RUN_SUBJECT_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA" - exit 1 - fi - if ! [ "$ACTUAL_HEAD" = "$EXPECTED_HEALED_SHA" ]; then - echo "::error::heal revalidation refused: checkout head $ACTUAL_HEAD does not equal expected healed head $EXPECTED_HEALED_SHA" - exit 1 - fi - echo "heal revalidation preflight: checkout names expected healed head $EXPECTED_HEALED_SHA" - fi - - name: Isolate toolchain homes - run: | - # dissolve-on: ci_toolchain_home_isolation_script -- orch-emitted foreign-executor prelude step wiping and setting HOME/CARGO_HOME/RUSTUP_HOME under RUNNER_TEMP so concurrent runner slots stop sharing one toolchain; leaf rm/echo strings remain until a typed per-job filesystem-and-environment effect lands on host_effect_apply (shell-to-intent Phase 2). This obligation covers THIS carrier and ci_isolate_toolchain_script, which share that terminal construction; ci_pin_rustup_default_script carries its own obligation because it does not - rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo" - echo "HOME=$RUNNER_TEMP" >> "$GITHUB_ENV" - echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" - echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" - - name: Install Rust toolchain - uses: actions-rust-lang/setup-rust-toolchain@v1.16.0 - with: - components: rustfmt - cache: false - - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) - run: | - # dissolve-on: ci_pin_rustup_default_script -- orch-emitted foreign-executor step selecting a rustup default toolchain inside an isolated RUSTUP_HOME, which starts with none, and resolving the cargo binary that selection implies. The leaf rustup/command/echo strings remain until a typed TOOLCHAIN-SELECTION effect lands on host_effect_apply -- NOT the filesystem-and-environment effect ci_toolchain_home_isolation_script waits on, which is why this is a separate obligation: that effect landing alone would leave this carrier standing - rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" - if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi - if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi - echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" - - name: Build the witness fold - id: build_witness_fold - run: | - cargo build --release -p v1-compiler --bin claim_executor --bin gunbc - - 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: "Required CI: v2-native lane (emitted-native compiler executes the derived v2.test.* universe)" - run: | - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - cd "$ROOT" - 'target/release/claim_executor' '--required-ci' '--source-root' 'dag' '--source-root' 'src/v2' '--required-lane' 'v2-native' - if: "!cancelled() && steps.build_witness_fold.outcome == 'success'" - - name: Record runner filesystem at job end - run: | - # dissolve-on: toolchain_filesystem_probe -- delete the start/end runner-filesystem instrument after its joined readings identify and the fleet fixes the toolchain deleter, OR after per-job runner microVMs make the shared filesystem eviction class impossible - GUNBC_TOOLCHAIN_FS_PREFIX='GUNBC_TOOLCHAIN_FS phase=end' - echo "$GUNBC_TOOLCHAIN_FS_PREFIX time_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo unavailable) host=$(hostname 2>/dev/null || echo unavailable) runner=${RUNNER_NAME:-unset}" - echo "$GUNBC_TOOLCHAIN_FS_PREFIX runner_temp=${RUNNER_TEMP:-unset} rustup_home=${RUSTUP_HOME:-unset} cargo_home=${CARGO_HOME:-unset} uptime_seconds=$(cut -d' ' -f1 /proc/uptime 2>/dev/null || echo unavailable)" - echo "$GUNBC_TOOLCHAIN_FS_PREFIX loadavg=$(tr ' ' ',' < /proc/loadavg 2>/dev/null || echo unavailable)" - df -Pk "${RUNNER_TEMP:-/path-that-does-not-exist}" 2>&1 | while IFS= read -r GUNBC_TOOLCHAIN_FS_LINE; do echo "$GUNBC_TOOLCHAIN_FS_PREFIX df_blocks=$GUNBC_TOOLCHAIN_FS_LINE"; done - df -Pik "${RUNNER_TEMP:-/path-that-does-not-exist}" 2>&1 | while IFS= read -r GUNBC_TOOLCHAIN_FS_LINE; do echo "$GUNBC_TOOLCHAIN_FS_PREFIX df_inodes=$GUNBC_TOOLCHAIN_FS_LINE"; done - for GUNBC_TOOLCHAIN_FS_PATH in "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains/*/bin/rustc "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains/*/bin/rustfmt "${RUNNER_TEMP:-/path-that-does-not-exist}"/cargo/bin/cargo; do if [ -e "$GUNBC_TOOLCHAIN_FS_PATH" ]; then GUNBC_TOOLCHAIN_FS_STAT=$(stat -c 'size=%s mtime_epoch=%Y mtime=%y inode=%i' "$GUNBC_TOOLCHAIN_FS_PATH" 2>&1 || echo stat-unavailable); echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=file path=$GUNBC_TOOLCHAIN_FS_PATH exists=yes $GUNBC_TOOLCHAIN_FS_STAT"; else echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=file path=$GUNBC_TOOLCHAIN_FS_PATH exists=no size=unavailable mtime_epoch=unavailable mtime=unavailable inode=unavailable"; fi; done - for GUNBC_TOOLCHAIN_FS_PATH in "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains/*/bin "${RUNNER_TEMP:-/path-that-does-not-exist}"/rustup/toolchains "${RUNNER_TEMP:-/path-that-does-not-exist}"/cargo/bin; do if [ -d "$GUNBC_TOOLCHAIN_FS_PATH" ]; then GUNBC_TOOLCHAIN_FS_STAT=$(stat -c 'mtime_epoch=%Y mtime=%y inode=%i' "$GUNBC_TOOLCHAIN_FS_PATH" 2>&1 || echo stat-unavailable); echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=directory path=$GUNBC_TOOLCHAIN_FS_PATH exists=yes $GUNBC_TOOLCHAIN_FS_STAT"; else echo "$GUNBC_TOOLCHAIN_FS_PREFIX kind=directory path=$GUNBC_TOOLCHAIN_FS_PATH exists=no mtime_epoch=unavailable mtime=unavailable inode=unavailable"; fi; done - if: always() witnesses: runs-on: [self-hosted, linux, arm64] - needs: [required-witnesses-build, required-witnesses-floor, required-v2-native] + needs: [required-witnesses-build, required-witnesses-floor] timeout-minutes: 5 if: always() steps: @@ -497,17 +415,16 @@ jobs: if '[' "$EVENT_NAME" '=' 'pull_request' ']'; then HEAD_STANDING='unobserved'; fi 'echo' 'required floor attempt: pull_request='"$PULL_REQUEST"' measured_head='"$MEASURED_HEAD"' run_id='"$RUN_ID"' head_standing='"$HEAD_STANDING" VERDICT='stands-green' - if '[' "$BUILD" '!=' 'success' ']' || '[' "$FLOOR" '!=' 'success' ']' || '[' "$V2_NATIVE" '!=' 'success' ']'; then VERDICT='stands-unestablished'; fi - if '[' "$BUILD" '=' 'failure' ']' || '[' "$FLOOR" '=' 'failure' ']' || '[' "$V2_NATIVE" '=' 'failure' ']'; then VERDICT='stands-red'; fi + if '[' "$BUILD" '!=' 'success' ']' || '[' "$FLOOR" '!=' 'success' ']'; then VERDICT='stands-unestablished'; fi + if '[' "$BUILD" '=' 'failure' ']' || '[' "$FLOOR" '=' 'failure' ']'; then VERDICT='stands-red'; fi MECHANISM='unestablished' ATTRIBUTION='unestablished' - 'echo' 'required lanes: build='"$BUILD"' floor='"$FLOOR"' v2-native='"$V2_NATIVE"' verdict='"$VERDICT"' mechanism='"$MECHANISM"' attribution='"$ATTRIBUTION" - if '[' "$VERDICT" '=' 'stands-red' ']'; then 'echo' '::error::a required lane concluded failure; that alone does not establish whether its subject was evaluated, so read the log before assuming a defect in the diff (build='"$BUILD"' floor='"$FLOOR"' v2-native='"$V2_NATIVE"' mechanism='"$MECHANISM"' attribution='"$ATTRIBUTION"') - open that job'\''s log' >&2; exit 1; fi - if '[' "$VERDICT" '=' 'stands-unestablished' ']'; then 'echo' '::error::a required lane produced no conclusion of its own, so this head'\''s floor verdict is unobservable rather than failed; rerun it (build='"$BUILD"' floor='"$FLOOR"' v2-native='"$V2_NATIVE"' mechanism='"$MECHANISM"' attribution='"$ATTRIBUTION"') - open that job'\''s log' >&2; exit 1; fi + 'echo' 'required lanes: build='"$BUILD"' floor='"$FLOOR"' verdict='"$VERDICT"' mechanism='"$MECHANISM"' attribution='"$ATTRIBUTION" + if '[' "$VERDICT" '=' 'stands-red' ']'; then 'echo' '::error::a required lane concluded failure; that alone does not establish whether its subject was evaluated, so read the log before assuming a defect in the diff (build='"$BUILD"' floor='"$FLOOR"' mechanism='"$MECHANISM"' attribution='"$ATTRIBUTION"') - open that job'\''s log' >&2; exit 1; fi + if '[' "$VERDICT" '=' 'stands-unestablished' ']'; then 'echo' '::error::a required lane produced no conclusion of its own, so this head'\''s floor verdict is unobservable rather than failed; rerun it (build='"$BUILD"' floor='"$FLOOR"' mechanism='"$MECHANISM"' attribution='"$ATTRIBUTION"') - open that job'\''s log' >&2; exit 1; fi env: BUILD: ${{ needs['required-witnesses-build'].result }} FLOOR: ${{ needs['required-witnesses-floor'].result }} - V2_NATIVE: ${{ needs['required-v2-native'].result }} PULL_REQUEST: ${{ github.event.pull_request.number }} EVENT_NAME: ${{ github.event_name }} MEASURED_HEAD: ${{ github.event.pull_request.head.sha }} diff --git a/dag/gunbc/fabric/fabric_witness_run.dag b/dag/gunbc/fabric/fabric_witness_run.dag index 46af6e1d325..7d75ec6e013 100644 --- a/dag/gunbc/fabric/fabric_witness_run.dag +++ b/dag/gunbc/fabric/fabric_witness_run.dag @@ -20,7 +20,7 @@ import product.fabric.work { work_for, } import std.content_hash { ContentHash, content_hash_of_value, serialize_content_hash } -import gunbc.required_ci_phase_roster { BuildLane, WitnessesLane, V2NativeLane, required_ci_lane_name } +import gunbc.required_ci_phase_roster { BuildLane, WitnessesLane, required_ci_lane_name } import extdeps.git.object_store { GitObjectId, git_object_id_wire_hex, git_object_id_content_hash, git_sha1_object_id } import product.fabric.supply { SupplierOffer, OfferFungibility, DemandOfferAffordability, GrantDuration, EstimatedGrantDuration, @@ -555,15 +555,6 @@ fn build_lane_run_command() -> ArgvCommand { required_ci_lane_command(lane: required_ci_lane_build, measurement_receipt: "") } -data required_ci_lane_v2_native: String = required_ci_lane_name(l: V2NativeLane) - -// The v2-native lane: the emitted-native compiler executes the derived v2.test.* universe -// through its own whole-source-root Eval driver. The job contract and admission authority are -// gunbc.witness_v2_native_route; this command is only the argv the workflow step runs. -fn v2_native_lane_run_command() -> ArgvCommand { - required_ci_lane_command(lane: required_ci_lane_v2_native, measurement_receipt: "") -} - // THE FLOOR IS MODELED AS AN ORDINARY PRICED DEMAND. NO PRICE IS CLEARED TODAY. // // The intent is unchanged: gunbc's own CI enters the market on the same terms as any other demand, diff --git a/dag/gunbc/required_ci_phase_roster.dag b/dag/gunbc/required_ci_phase_roster.dag index c4f5c6f58bd..f2beab6a618 100644 --- a/dag/gunbc/required_ci_phase_roster.dag +++ b/dag/gunbc/required_ci_phase_roster.dag @@ -21,21 +21,20 @@ import std.types { String, Bool, List } // cannot read one). The pair join carries two further refusals: a selected lane whose // authority-expected phase set is empty stops rather than greening over zero phases, and the // observed ran set must equal the authority-expected set exactly. Lane ownership is load-bearing -// now that the workflow is three independently selected lanes with the native route isolated in -// its own. The terminal state is the census's dissolution row: the workflow emission consumes +// because the lanes are independently selected jobs (the native route was a third lane from +// 2026-09-09 to 2026-09-11; its deletion is gunbc.rung_drop v2_native_route_off_the_merge_path). +// The terminal state is the census's dissolution row: the workflow emission consumes // this roster whole and the host enum, lane match and REQUIRED_CI_PHASES list delete atomically. // Until then the parallel representation is this module's declared debt and the per-run joins are // its wall. type RequiredCiLane = BuildLane | WitnessesLane - | V2NativeLane fn required_ci_lane_name(l: RequiredCiLane) -> String { match l { BuildLane => "build" WitnessesLane => "witnesses" - V2NativeLane => "v2-native" } } @@ -64,7 +63,6 @@ type RequiredCiPhase | GeneratedArtifactPhase | RegenFixedPointPhase | FloorPhase - | V2NativePhase // TWO NAMED ARMS, NEVER A WIDENED PATH PREDICATE. A path that neither authority owns is in // neither arm; inability to produce either arm refuses GeneratedArtifactPhase. The registry arm @@ -110,7 +108,6 @@ fn required_ci_phase_name(p: RequiredCiPhase) -> String { GeneratedArtifactPhase => "generated-artifact" RegenFixedPointPhase => "regen-fixed-point" FloorPhase => "floor" - V2NativePhase => "v2-native" } } @@ -123,7 +120,6 @@ fn required_ci_phase_lane(p: RequiredCiPhase) -> RequiredCiLane { GeneratedArtifactPhase => BuildLane RegenFixedPointPhase => BuildLane FloorPhase => WitnessesLane - V2NativePhase => V2NativeLane } } @@ -137,7 +133,6 @@ data required_ci_phases: List = [ GeneratedArtifactPhase, RegenFixedPointPhase, FloorPhase, - V2NativePhase, ] fn required_ci_phase_is(a: RequiredCiPhase, b: RequiredCiPhase) -> Bool { diff --git a/dag/gunbc/rung_drop/roster.dag b/dag/gunbc/rung_drop/roster.dag index 160a8bd01fc..e4bb3c6fe93 100644 --- a/dag/gunbc/rung_drop/roster.dag +++ b/dag/gunbc/rung_drop/roster.dag @@ -59,6 +59,7 @@ import gunbc.rung_drop.deleted_cadence_reference_drop { deleted_cadence_referenc import gunbc.rung_drop.floor_cost_debt_straddling_produced_decl { floor_cost_debt_straddling_produced_decl } import gunbc.rung_drop.determinism_transitive_reachability { determinism_transitive_reachability } import gunbc.rung_drop.transitional_admission_exception { transitional_admission_exception } +import gunbc.rung_drop.v2_native_route_off_the_merge_path { v2_native_route_off_the_merge_path } data rung_drop_roster: List = [ floor_cut_heal, @@ -101,6 +102,7 @@ data rung_drop_roster: List = [ floor_cost_debt_straddling_produced_decl, determinism_transitive_reachability, transitional_admission_exception(), + v2_native_route_off_the_merge_path, ] // THE DERIVATION THE PROJECTION USES, so "standing today" has one authority and not two. The 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 new file mode 100644 index 00000000000..a1091c01e1e --- /dev/null +++ b/dag/gunbc/rung_drop/v2_native_route_off_the_merge_path.dag @@ -0,0 +1,54 @@ +module gunbc.rung_drop.v2_native_route_off_the_merge_path + +import std.types { List, NonEmptyStr } +import gunbc.rung_drop { RungDrop, Standing, TypedDeclaration, DeletedWithoutReplacement } +import gunbc.guarantee_rung { Mitigatable, MechanicallyPreventable } + +// DECLARED 2026-09-11 (operator ruling, session fierce-lark-661). The required `v2-native` job +// was authorized 2026-09-09 as one additional required lane and landed with #10882 (main at +// 0006e0aabd): the emitted-native v2 compiler executes the derived v2.test.* universe plus four +// controls, and gunbc.witness_v2_native_route admits or refuses the minted receipt. It ran on +// every push and pull request until this change deleted it. +// +// WHY THE JOB IS DELETED RATHER THAN RETUNED. Its declared envelope was 180 minutes citing a +// ~70-minute native run; the first full run on srv2 (2fb67b411a) took 4h06m wall at 14.8 GB +// peak RSS, and the shared arm64 runners are slower still. The workflow's concurrency group is +// keyed per pull-request number or per run id with cancel-in-progress false, so no push ever +// superseded a RUNNING native job and every push to main added another ~4h claimant. The +// measured harm on 2026-09-11: 31 concurrent required-v2-native jobs, and the two lanes that +// gate (build, floor) failing under memory stalls and changed-witness budget interruptions on +// main itself. A required check whose wall exceeds its envelope on every head is not a weak +// wall; it is a check that never concludes, and it was displacing the checks that do. +// +// WHAT IS NOT DROPPED. The route model (gunbc.witness_v2_native_route), its harness +// (v1_compiler.cli_run.native_lane_runner) and the admission contract are unchanged and remain +// reachable as `claim_executor --v2-native-route` — an operator-invoked instrument, the same +// producer the lane ran, so a receipt minted by hand and one the lane would have minted cannot +// be two facts. What is lost is EXECUTION ON THE ACCEPTANCE PATH: nothing runs the native route +// on a merge candidate unless a person chooses to. + +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", + "the derived v2.test.* universe executed by the emitted-native compiler (positive population)", + "v2.native_lane_fixture.control native_lane_false_control / native_lane_true_control (live-verdict controls)", + "fixtures/native_lane_malformed/poison.dag.poisoned (malformed-specimen control)", + "the old-route-withdrawn (no-fallback) control", +] + +data v2_native_route_off_the_merge_path: RungDrop = RungDrop { + identity: "v2_native_route_off_the_merge_path" as NonEmptyStr, + + subject: "The v2 native route (emitted-native compiler over the derived v2.test.* universe) as a required CI lane", + + declared: "2026-09-11", + + standing: Standing, + + declaration: TypedDeclaration { + previous: MechanicallyPreventable, + temporary: Mitigatable, + reason: DeletedWithoutReplacement, + population: v2_native_route_off_the_merge_path_population, + restoration_trigger: "A REQUIRED NATIVE-ROUTE JOB WHOSE WALL FITS THE ACCEPTANCE PATH, designed from the ground up against an operator-agreed contract and not re-added into the same envelope: the job must conclude on every merge candidate inside its declared timeout on the real runner class (a measured wall, not a cited one), a newer head of the same subject must supersede the older run so no head holds more than one native claimant, and its red must still discriminate every clause of native_route_admission — universe join, positive population, classified refusals, per-identity agreement with the floor reference, and all four controls. Whether that is reached by affected-set admission that runs only the universe a change touches, by a native run cheap enough to fit whole, or by a job that runs on a different cadence with its own row, is the design decision this drop waits on; the first two retire this row, the third replaces it with a differently-named drop. Re-adding `required-v2-native` with its 2026-09-10 shape does not retire this row and is refused by test.claim.witness_floor_workflow_consolidation_witness_test w_RED_the_deleted_lanes_do_not_return", + } +} diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index fea864cf13f..5f568f5a9bb 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -69,8 +69,7 @@ import gunbc.ci_spec { import gunbc.repo_identity { gunbc_default_branch_name } import gunbc.merge_admission_produce { ci_repo_root_shell } import gunbc.ci_layer_roots { witness_layer_roots } -import gunbc.fabric_witness_run { witnesses_lane_run_command, witnesses_lane_measurement_command, build_lane_run_command, v2_native_lane_run_command } -import gunbc.witness_v2_native_route { required_v2_native_job_name } +import gunbc.fabric_witness_run { witnesses_lane_run_command, witnesses_lane_measurement_command, build_lane_run_command } import extdeps.exec.command { ArgvCommand, shell_command_render } import gunbc.required_lanes_gate { RequiredLane, required_lanes_gate_stmts } import extdeps.github.expressions { ContextAccess, Github, Needs, interpolate } @@ -1676,104 +1675,17 @@ fn witness_floor_job() -> Job { } } -// THE NATIVE LANE JOB (operator authorization 2026-09-09: ONE additional required job, wired — -// not an enrolment promise). The three questions a new job must answer before it claims a paid -// runner on every push: -// -// THE MEASURED WALL. The lane pays: the shared prepared steps (toolchain + two-bin seed build), -// the v2 closure emission, one cargo build of the emitted compiler crate, the universe -// derivation fold (the floor's own discovery producer over the full module inventory), and the -// native run itself — one process over the derived v2.test.* universe plus the three controls. -// Measured 2026-09-10 on the 4-core cloud VM, after the lexer accumulation fix -// (v2.compiler.tokenize lexeme_grow): the native run dominates the envelope — ~70 minutes for -// the whole-corpus context fold plus the 3467-row eval, observed by the -// gunbc.witness_v2_native_route census — beside ~10 minutes of emission and ~10 minutes of -// cargo build. The envelope is 180 minutes, the floor lane's own value, so the merge queue's -// check_response_timeout (witness_floor_lane_timeout plus margin) still bounds it. -// -// WHAT RED DISCRIMINATES. The admission authority is gunbc.witness_v2_native_route: the observed -// population must join the derived universe exactly (no duplicates, no foreign rows, no -// omissions), the positive population must be non-empty, every refusal must be classified from -// stage and provenance, every native verdict must agree with the floor-derived reference at the -// same identity, and all four controls must hold — the false control observed false, the true -// control observed passed, the malformed specimen refused, the old route withdrawn. A step -// folded into an existing lane could not carry this: its red would land on that lane's receipt, -// conflating two routes' subjects. -// -// WHY A JOB AND NOT A STEP. The no-fallback control withdraws the old compiler/interpreter route -// after the emitted compiler exists; the floor lane's own execution IS the old route, so the -// control and the floor cannot share a job. The lane also builds a second crate (the emitted -// compiler), a capability envelope the existing lanes do not declare. A job is the smallest -// boundary that keeps both facts honest. -data v2_native_lane_timeout: Minute = minute(count: 180) - -fn v2_native_lane_build_script() -> String { - repo_self_build_command(bins: witness_floor_required_bins) -} - -fn v2_native_lane_run_script() -> String { - required_lane_run_script(command: v2_native_lane_run_command()) -} - -fn v2_native_lane_run_step() -> Step { - RunStep { - name: Present { value: "Required CI: v2-native lane (emitted-native compiler executes the derived v2.test.* universe)" }, - id: none, - run: v2_native_lane_run_script(), - shell: none, - env: none, - working_directory: none, - if_condition: Present { value: witness_floor_precondition() }, - continue_on_error: none, - timeout_minutes: none - } -} - -fn v2_native_lane_bound_steps() -> List { - append( - prepared_bound_steps(build_script: v2_native_lane_build_script()), - items: [ - WitnessFloorBoundStep { - step: witness_toolchain_homes_isolated_guard_step(), - role: capability_neutral, - step_name: "Require isolated toolchain homes", - }, - WitnessFloorBoundStep { - step: v2_native_lane_run_step(), - role: consumes_only(capabilities: [CargoCapability, RustcCapability, RustfmtCapability]), - step_name: "Required CI: v2-native lane (emitted-native compiler executes the derived v2.test.* universe)", - }, - WitnessFloorBoundStep { - step: witness_toolchain_filesystem_end_probe_step(), - role: capability_neutral, - step_name: "Record runner filesystem at job end", - }, - ], - ) -} - -fn v2_native_lane_steps() -> List { - list_map(xs: v2_native_lane_bound_steps(), f: fn(b) { b.step }) -} - -fn v2_native_lane_needs() -> List { [] } - -fn v2_native_lane_job() -> Job { - Job { - id: required_v2_native_job_name, - name: none, - runner: gunbc_ci_selected_runner_spec(), - steps: v2_native_lane_steps(), - needs: v2_native_lane_needs(), - env: none, - outputs: none, - if_condition: none, - timeout_minutes: minute_count(v2_native_lane_timeout), - continue_on_error: none, - concurrency: none, - permissions: none - } -} +// THE NATIVE LANE JOB IS DELETED (operator ruling, 2026-09-11). It was authorized 2026-09-09 as +// one additional required job and ran on every push and pull request from #10882 (0006e0aabd) +// until this change. Its measured wall was 4h06m on srv2 against a declared 180-minute envelope +// that cited ~70 minutes; the workflow's concurrency group is keyed per PR number or run id with +// cancel-in-progress false, so no push superseded a running native job and every main push +// added another ~4h claimant on the shared arm64 runners, starving the build and floor lanes +// that gate. The route model (gunbc.witness_v2_native_route) and its harness +// (`claim_executor --v2-native-route`) stay as an operator-invoked instrument; the loss of the +// merge-path lane is the declared drop gunbc.rung_drop v2_native_route_off_the_merge_path, +// and a replacement job is designed from the ground up against that row's trigger, never +// re-added into the same envelope. // THE LANE'S EXECUTION ENVELOPE, NAMED BECAUSE A SECOND CONSUMER NOW READS IT. The value did not // change; it stopped being a literal. gunbc.repo_ruleset derives the merge queue's @@ -2379,7 +2291,6 @@ fn required_lanes_run_id_expression() -> String { // all. A `Rejected` fold can therefore never reach a published step. data required_lanes_gate_build_var: String = "BUILD" data required_lanes_gate_floor_var: String = "FLOOR" -data required_lanes_gate_v2_native_var: String = "V2_NATIVE" // THE REQUIRED LANE ROSTER, AND IT IS ONE ROSTER READ TWICE RATHER THAN TWO LISTS THAT AGREE. // @@ -2404,11 +2315,6 @@ fn required_lanes_roster() -> FreeSemigroup { job_id: floor_lane_job_id, label: "floor", var_name: required_lanes_gate_floor_var - }, - RequiredLane { - job_id: required_v2_native_job_name, - label: "v2-native", - var_name: required_lanes_gate_v2_native_var } ] } @@ -2642,12 +2548,12 @@ fn witness_floor_triggers() -> List { // THE AGGREGATE IS DELIBERATELY NOT A MEMBER. It checks out nothing and runs no witness: it reads // the other lanes' results. Every consumer of this list is asking about lanes that carry a subject, // and the aggregate joined that population only as something to filter back out. -type WitnessFloorLane = WitnessFloorBuildLane | WitnessFloorFloorLane | WitnessFloorHealLane | WitnessFloorV2NativeLane +type WitnessFloorLane = WitnessFloorBuildLane | WitnessFloorFloorLane | WitnessFloorHealLane fn witness_floor_lane_roster() -> FreeSemigroup { FreeSemigroup { head: WitnessFloorBuildLane, - tail: [WitnessFloorFloorLane, WitnessFloorHealLane, WitnessFloorV2NativeLane] + tail: [WitnessFloorFloorLane, WitnessFloorHealLane] } } @@ -2656,7 +2562,6 @@ fn witness_floor_lane_job(lane: WitnessFloorLane) -> Job { WitnessFloorBuildLane => build_lane_job() WitnessFloorFloorLane => witness_floor_job() WitnessFloorHealLane => heal_generated_artifacts_job() - WitnessFloorV2NativeLane => v2_native_lane_job() } } @@ -2675,7 +2580,6 @@ fn witness_floor_lane_job_id(lane: WitnessFloorLane) -> String { WitnessFloorBuildLane => build_lane_job_id WitnessFloorFloorLane => floor_lane_job_id WitnessFloorHealLane => heal_generated_artifacts_job_id - WitnessFloorV2NativeLane => required_v2_native_job_name } } diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 37df9718e0f..0970c786433 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,8 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, v2_native_lane_needs } -import gunbc.witness_v2_native_route { required_v2_native_job_name } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } import gunbc.fleet_converge_workflow { fleet_converge_workflow } @@ -254,20 +253,17 @@ test fn w_RED_floor_measurement_publishes_before_separate_adjudication() -> Bool test fn w_RED_lane_contexts_collide_with_no_other_emitted_workflow() -> Bool { !any(fleet_converge_workflow.jobs, j => j.id == build_lane_job_id) && !any(fleet_converge_workflow.jobs, j => j.id == floor_lane_job_id) - && !any(fleet_converge_workflow.jobs, j => j.id == required_v2_native_job_name) && build_lane_job_id != floor_lane_job_id - && build_lane_job_id != required_v2_native_job_name && build_lane_job_id != witness_floor_workflow_job_id - && floor_lane_job_id != required_v2_native_job_name && floor_lane_job_id != witness_floor_workflow_job_id - && required_v2_native_job_name != witness_floor_workflow_job_id } // NO LANE WAITS ON ANOTHER, WHICH IS THE ENTIRE CONTENT OF "PARALLEL" -- and the // aggregation job waits on all of them, which is what keeps the split fail-closed. The -// roster was two lanes at the split (build, floor); the required v2-native lane's enrolment -// (gunbc.witness_v2_native_route) makes it three, and the witness name keeps the split's -// original identity rather than re-authoring the row. +// roster was two lanes at the split (build, floor); the required v2-native lane made it three +// from 2026-09-09 until the 2026-09-11 ruling deleted that job (gunbc.rung_drop +// v2_native_route_off_the_merge_path), so it is two again, and the witness name keeps the +// split's original identity rather than re-authoring the row. // // THE EARLIER FORM OF THIS ROW ASSERTED THAT THE FILE CONTAINED NO `needs:` AT ALL, and that // was the right claim for a workflow shape that was WRONG. Review of gunbc#9203 found it: the @@ -279,7 +275,7 @@ test fn w_RED_lane_contexts_collide_with_no_other_emitted_workflow() -> Bool { // // The claim is therefore stated at the grain that actually matters. The typed lane jobs must // carry no `needs`, while the one roster from which the aggregate derives its `needs` must carry -// exactly the three lane identities. Reading those narrow producers avoids serializing the aggregate's +// exactly the two lane identities. Reading those narrow producers avoids serializing the aggregate's // large gate program merely to recover fields that existed before serialization. This remains a // strictly sharper discriminator than the old token count: it distinguishes the serialization // being forbidden from the aggregation being demanded, which "no needs anywhere" could not. @@ -289,8 +285,7 @@ test fn w_RED_lane_contexts_collide_with_no_other_emitted_workflow() -> Bool { test fn w_RED_neither_lane_waits_on_the_other() -> Bool { length(xs: build_lane_needs()) == 0 && length(xs: witness_floor_lane_needs()) == 0 - && length(xs: v2_native_lane_needs()) == 0 - && length(xs: non_empty_to_list(xs: required_lanes_roster())) == 3 + && length(xs: non_empty_to_list(xs: required_lanes_roster())) == 2 && count_where( xs: non_empty_to_list(xs: required_lanes_roster()), predicate: fn(lane) { lane.job_id == build_lane_job_id }, @@ -299,10 +294,6 @@ test fn w_RED_neither_lane_waits_on_the_other() -> Bool { xs: non_empty_to_list(xs: required_lanes_roster()), predicate: fn(lane) { lane.job_id == floor_lane_job_id }, ) == 1 - && count_where( - xs: non_empty_to_list(xs: required_lanes_roster()), - predicate: fn(lane) { lane.job_id == required_v2_native_job_name }, - ) == 1 } // THE FABRIC LANE STILL RUNS AND NO LONGER GATES, AND THIS ROW ASSERTS BOTH HALVES. @@ -321,7 +312,10 @@ test fn w_RED_neither_lane_waits_on_the_other() -> Bool { // // PERMANENT AFTER OBSERVED RED (DESIGN 4b(4)). Run 33924210916 supplied `fabric-evidence` and // observed this exact identity return Bool(false). This probe is therefore a regression control, -// not scaffolding: keep it enrolled for as long as any of the three citing rung drops stands. +// not scaffolding: keep it enrolled for as long as any of the citing rung drops stands. +// `required-v2-native` joined the deleted set 2026-09-11 (gunbc.rung_drop +// v2_native_route_off_the_merge_path): its ~4h wall on every push, with no supersession, +// starved the two lanes that gate. Re-adding it into the same envelope authors RED here. // POSITIVE-CONTROL SEAM: the countable must author RED when a deleted lane is supplied. Without // this input boundary, a planned/pass receipt would not distinguish a live absence wall from a // vacuous projection that always returned an empty list. @@ -330,14 +324,16 @@ fn deleted_lane_job_ids_are_absent(job_ids: List) -> Bool { job_id == "fabric-evidence" || job_id == "rust-unit-tests" || job_id == "emit-copy-qualification-battery" + || job_id == "required-v2-native" }) } test fn w_RED_the_deleted_lanes_do_not_return() -> Bool { deleted_lane_job_ids_are_absent(job_ids: witness_floor_lane_job_ids()) && !deleted_lane_job_ids_are_absent(job_ids: ["fabric-evidence"]) + && !deleted_lane_job_ids_are_absent(job_ids: ["required-v2-native"]) && !any(xs: non_empty_to_list(xs: required_lanes_roster()), predicate: fn(lane) { - lane.var_name == "FABRIC_EVIDENCE" + lane.var_name == "FABRIC_EVIDENCE" || lane.var_name == "V2_NATIVE" }) } diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 987488b876e..cb898180289 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -205,3 +205,7 @@ Transitive determinism reachability over the fn-arrow call graph: RUNG DROP, mec ### admission rows on falsifier-family cadences with no scheduled route, awaiting transition to a cadence that executes — declared 2026-09-08 admission rows on falsifier-family cadences with no scheduled route, awaiting transition to a cadence that executes: RUNG DROP, mitigatable -> outside the ladder (lost as a passenger of .github/workflows/falsifier.yml, deleted at 611fd02770 (#8283)). Population: dag/test/claim/method_arg_declared_contract_witness_test.dag::w_method_arg_infers_against_declared_contract_not_element_type_test, src/v2/test/claim/manual/english_emit_add_test.dag::english_emit_add_ingest_round_trip_holds, dag/test/claim/legacy_test_behavior_disposition_acceptance_test.dag::legacy_test_behavior_unclassified_frontier_is_zero, dag/test/claim/observation_raw_print_retirement_acceptance_test.dag::observation_emit_frontier_is_zero, dag/test/claim/long/g2_data_reference_under_selection_witness_test.dag::specimen_classifies_as_runtime_read, dag/test/claim/long/g2_data_reference_under_selection_witness_test.dag::specimen_does_not_classify_as_local_read, dag/test/claim/source_integration_proof_kernel_acceptance_test.dag::witness_p1_proof_kernel_acceptance_contract_holds, dag/test/claim/namespace_reference_derived_closure_acceptance_test.dag::witness_namespace_reference_derived_closure_closing_contract_holds, src/v2/test/claim/long/direct_rust_door_production_group_test.dag::direct_rust_door_production_group_closing_expectation_holds, dag/test/claim/self_host_parse_engine_hooks_behavioral_witness_test.dag::self_host_parse_engine_hooks_behavioral_receipt_holds, dag/test/claim/self_host_use_site_verdict_behavioral_witness_test.dag::self_host_use_site_verdict_behavioral_receipt_holds, dag/test/claim/self_host_body_producer_behavioral_witness_test.dag::self_host_body_producer_behavioral_receipt_holds, dag/test/claim/self_host_discovery_enumeration_behavioral_witness_test.dag::self_host_discovery_enumeration_behavioral_receipt_holds, dag/test/claim/self_host_target_carriers_behavioral_witness_test.dag::self_host_target_carriers_behavioral_receipt_holds, dag/test/claim/self_host_03_normalize_behavioral_witness_test.dag::self_host_03_normalize_behavioral_receipt_holds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_live_scope_fixture_origin_discoverable_in_entry_closure_reds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_live_structural_fixture_mint_site_discovered_reds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_ingest_mint_surface_requires_call_occurrence_frontier_reds, src/v2/test/claim/long/production_qualification_origin_probe_witness_test.dag::witness_direct_door_smoke_requires_closure_or_call_occurrence_frontier_reds, src/v2/test/claim/self_host/compiler_closure_emit_from_ingest_test.dag::compiler_closure_scoped_ingest_module_count_ok_holds, src/v2/test/claim/self_host/compiler_closure_emit_from_ingest_test.dag::compiler_closure_ingest_overflow_refuses_never_empty_holds, src/v2/test/claim/self_host/compiler_closure_ingest_boundary_witness_test.dag::compiler_closure_ingest_boundary_exact_cap_transport_holds, src/v2/test/claim/long/infer_transform_binary_infix_witness_test.dag::infer_transform_add_vertical_witness_holds. Restored when: each named identity carries a cadence with a live scheduled route, at which point each row above is removed from transitional_admission_exception_identities and the identity's exception in explicit_witness_admission.dag is deleted (the gate consumes the list, so removing from the list retires the exception). The cadence category those routes need is gunbc.rung_drop.shared_capability missing_cadence_category; the remainder is the per-identity migration onto a live route. Waiters whose remaining unsatisfied condition is only that capability retire together. When the category fires and any named identity still lacks a live scheduled route, this identity is removed from missing_cadence_category.waiter_identities in the SAME change as those retirements (delist, not SplitRetirement) and this row stays Standing until every named identity has that route. If every named identity already has a live scheduled route when the category fires, retire with the enrolled set.. + +### The v2 native route (emitted-native compiler over the derived v2.test.* universe) as a required CI lane — declared 2026-09-11 + +The v2 native route (emitted-native compiler over the derived v2.test.* universe) as a required CI lane: RUNG DROP, mechanically preventable -> mitigatable (deleted without replacement). Population: gunbc.witness_v2_native_route native_route_admission — every receipt clause, on every merge candidate, the derived v2.test.* universe executed by the emitted-native compiler (positive population), v2.native_lane_fixture.control native_lane_false_control / native_lane_true_control (live-verdict controls), fixtures/native_lane_malformed/poison.dag.poisoned (malformed-specimen control), the old-route-withdrawn (no-fallback) control. Restored when: A REQUIRED NATIVE-ROUTE JOB WHOSE WALL FITS THE ACCEPTANCE PATH, designed from the ground up against an operator-agreed contract and not re-added into the same envelope: the job must conclude on every merge candidate inside its declared timeout on the real runner class (a measured wall, not a cited one), a newer head of the same subject must supersede the older run so no head holds more than one native claimant, and its red must still discriminate every clause of native_route_admission — universe join, positive population, classified refusals, per-identity agreement with the floor reference, and all four controls. Whether that is reached by affected-set admission that runs only the universe a change touches, by a native run cheap enough to fit whole, or by a job that runs on a different cadence with its own row, is the design decision this drop waits on; the first two retire this row, the third replaces it with a differently-named drop. Re-adding `required-v2-native` with its 2026-09-10 shape does not retire this row and is refused by test.claim.witness_floor_workflow_consolidation_witness_test w_RED_the_deleted_lanes_do_not_return. diff --git a/src/v1/stage0/src/bin/claim_executor.rs b/src/v1/stage0/src/bin/claim_executor.rs index 2ef3095421a..def3f608ee4 100644 --- a/src/v1/stage0/src/bin/claim_executor.rs +++ b/src/v1/stage0/src/bin/claim_executor.rs @@ -270,6 +270,7 @@ fn run() -> Result { let mut required_ci_lane: Option = None; let mut required_v2_emission_mode = false; let mut required_emit_compile_mode = false; + let mut v2_native_route_mode = false; let mut required_regen_mode = false; let mut emit_partition_crates_mode = false; let mut emit_partition_crates_write = false; @@ -346,6 +347,15 @@ fn run() -> Result { "--required-emit-compile" => { required_emit_compile_mode = true; } + // THE NATIVE ROUTE AS AN OPERATOR-INVOKED INSTRUMENT, NOT A REQUIRED PHASE. It ran + // as the required `v2-native` lane from 2026-09-09 until the 2026-09-11 operator + // ruling deleted that job (~4h wall on every push against a 180-minute envelope; + // gunbc.rung_drop v2_native_route_off_the_merge_path). The harness and the + // admission authority are unchanged — this flag is the one route to them, so a + // receipt minted here and one minted by the deleted lane cannot be two facts. + "--v2-native-route" => { + v2_native_route_mode = true; + } "--required-regen" => { required_regen_mode = true; } @@ -1081,25 +1091,6 @@ fn run() -> Result { ran.push("floor"); } - // THE NATIVE ROUTE PHASE. The harness derives the v2.test.* universe with the floor's - // own discovery producer, prepares the emitted-native compiler through the emit-compile - // machinery, runs the universe plus controls through that binary by explicit path with - // the old route withdrawn, and hands the minted receipt to the admission authority — - // the phase greens exactly when gunbc.witness_v2_native_route admits it. - if required_ci_phase_selected(RequiredCiPhase::V2Native, required_ci_lane) { - eprintln!( - "required-ci: phase v2-native (emitted-native compiler executes the derived v2.test.* universe)" - ); - match v1_compiler::cli_run::run_required_v2_native(&source_roots) { - Ok(()) => {} - Err(e) => { - eprintln!("required-ci: v2-native refused: {e}"); - phase_failures.push(format!("v2-native refused: {e}")); - } - } - ran.push("v2-native"); - } - // THE OBSERVED RAN SET IS THE AUTHORITY-EXPECTED SET, EXACTLY. The census below prints // phases_run; this refusal covers the state where that number is legible but wrong — a // phase silently never reached, or one executed that the authority does not expect in @@ -1254,6 +1245,24 @@ fn run() -> Result { } } + if v2_native_route_mode { + let roots = if source_roots.is_empty() { + v1_compiler::cli_run::witness_layer_roots() + } else { + source_roots.clone() + }; + eprintln!( + "v2-native-route: emitted-native compiler executes the derived v2.test.* universe (operator-invoked; not a required lane)" + ); + return match v1_compiler::cli_run::run_required_v2_native(&roots) { + Ok(()) => Ok(ExitCode::SUCCESS), + Err(e) => { + eprintln!("v2-native-route: refused: {e}"); + Err(ExitCode::from(1)) + } + }; + } + if required_v2_emission_mode { let roots = if source_roots.is_empty() { v1_compiler::cli_run::witness_layer_roots() @@ -1614,7 +1623,6 @@ fn emit_worker_terminal_before_return(code: ExitCode) -> ExitCode { enum RequiredCiLane { Build, Witnesses, - V2Native, } impl RequiredCiLane { @@ -1622,7 +1630,6 @@ impl RequiredCiLane { match self { RequiredCiLane::Build => "build", RequiredCiLane::Witnesses => "witnesses", - RequiredCiLane::V2Native => "v2-native", } } @@ -1633,9 +1640,8 @@ impl RequiredCiLane { match value { "build" => Ok(RequiredCiLane::Build), "witnesses" => Ok(RequiredCiLane::Witnesses), - "v2-native" => Ok(RequiredCiLane::V2Native), other => Err(format!( - "unknown --required-lane: {other} (expected build | witnesses | v2-native)" + "unknown --required-lane: {other} (expected build | witnesses)" )), } } @@ -1648,7 +1654,6 @@ enum RequiredCiPhase { GeneratedArtifact, RegenFixedPoint, Floor, - V2Native, } impl RequiredCiPhase { @@ -1659,7 +1664,6 @@ impl RequiredCiPhase { RequiredCiPhase::RegenFixedPoint => "regen-fixed-point", RequiredCiPhase::GeneratedArtifact => "generated-artifact", RequiredCiPhase::Floor => "floor", - RequiredCiPhase::V2Native => "v2-native", } } @@ -1685,29 +1689,24 @@ impl RequiredCiPhase { // measurement to reference and nothing to compare. RequiredCiPhase::RegenFixedPoint => RequiredCiLane::Build, RequiredCiPhase::Floor => RequiredCiLane::Witnesses, - // THE NATIVE ROUTE IS ITS OWN LANE, NOT A FLOOR RIDER: the lane's no-fallback - // control withdraws the old compiler/interpreter route's CLI for the spawn window, - // and the floor lane's own execution IS that route -- the two cannot share a job - // (lane authority: gunbc.witness_v2_native_route). - RequiredCiPhase::V2Native => RequiredCiLane::V2Native, } } } -// THE REQUIRED GATE IS SIX PHASES. Four are the 2026-08-29 compiler-floor bankruptcy roster; +// THE REQUIRED GATE IS FIVE PHASES. Four are the 2026-08-29 compiler-floor bankruptcy roster; // generated-artifact returned after its declared exposure produced a real stale projection on // main, and now also owns the former regen phase's stage0-mirror population. Keeping two phase // identities would preserve the independently-green outcomes this composition removes. The other -// three removed phases remain outside required CI and inside the declared drop. The sixth, -// v2-native, is the 2026-09-09 operator-authorized native-route lane: the emitted-native -// compiler executes the derived v2.test.* universe (gunbc.witness_v2_native_route). -const REQUIRED_CI_PHASES: [RequiredCiPhase; 6] = [ +// three removed phases remain outside required CI and inside the declared drop. The v2-native +// phase (2026-09-09 to 2026-09-11) left required CI by operator ruling — its ~4h wall on every +// push starved the gating lanes; the route stays reachable as `--v2-native-route` and the drop +// is gunbc.rung_drop v2_native_route_off_the_merge_path. +const REQUIRED_CI_PHASES: [RequiredCiPhase; 5] = [ RequiredCiPhase::Parse, RequiredCiPhase::NamespaceWaveAdmission, RequiredCiPhase::GeneratedArtifact, RequiredCiPhase::RegenFixedPoint, RequiredCiPhase::Floor, - RequiredCiPhase::V2Native, ]; /// The `.dag` coproduct this enum realizes, and the declaration whose variants it must equal. @@ -1719,13 +1718,12 @@ const PHASE_ROSTER_AUTHORITY_MODULE: &str = "gunbc.required_ci_phase_roster"; const PHASE_ROSTER_AUTHORITY_DECL: &str = "RequiredCiPhase"; /// Every phase this binary realizes, in the authority's own variant spelling. -const PHASE_ROSTER_VARIANT_LABELS: [&str; 6] = [ +const PHASE_ROSTER_VARIANT_LABELS: [&str; 5] = [ "ParsePhase", "NamespaceWaveAdmissionPhase", "GeneratedArtifactPhase", "RegenFixedPointPhase", "FloorPhase", - "V2NativePhase", ]; /// Refuse if the host phase enum and the `.dag` phase roster disagree — the same both-directions @@ -1737,8 +1735,8 @@ const PHASE_ROSTER_VARIANT_LABELS: [&str; 6] = [ /// WHAT THIS JOIN DOES NOT COVER: lane ownership. A match arm is not a declaration, so the /// index this join reads cannot see which lane owns a phase. That half is joined by evaluation /// instead — `authority_lane_phase_rows` against this host's enum-plus-lane-match, executed at -/// run start in EVERY lane (three lanes are independently selected now, and the native route's -/// isolation is load-bearing), with the empty-expectation and exact-ran-set refusals beside it. +/// run start in EVERY lane (the lanes are independently selected jobs), with the +/// empty-expectation and exact-ran-set refusals beside it. fn phase_roster_findings( index: &v1_compiler::cli_run::declaration_index::DeclarationIndex, ) -> Vec { @@ -2316,17 +2314,12 @@ mod tests { lane_phase_row("build", "generated-artifact"), lane_phase_row("build", "regen-fixed-point"), lane_phase_row("witnesses", "floor"), - lane_phase_row("v2-native", "v2-native"), ] } #[test] fn the_standing_pairs_join_cleanly_in_every_lane() { - for lane in [ - RequiredCiLane::Build, - RequiredCiLane::Witnesses, - RequiredCiLane::V2Native, - ] { + for lane in [RequiredCiLane::Build, RequiredCiLane::Witnesses] { assert!( lane_roster_findings(&standing_authority_rows(), Some(lane)).is_empty(), "lane {} must join cleanly", @@ -2339,21 +2332,23 @@ mod tests { #[test] fn a_lane_ownership_divergence_is_red_and_names_the_pair() { // The review's scenario — the host mapping and the authority mapping disagree about - // which lane owns v2-native — simulated by moving the authority row; the pair - // difference is symmetric, so either side's edit fires both directions. + // which lane owns a phase — simulated by moving every build-lane row to witnesses; + // the pair difference is symmetric, so either side's edit fires both directions, and + // the build lane's expectation is now empty and must refuse. let mut rows = standing_authority_rows(); - rows.retain(|r| r.phase != "v2-native"); - rows.push(lane_phase_row("build", "v2-native")); - let findings = lane_roster_findings(&rows, Some(RequiredCiLane::V2Native)); + rows.retain(|r| r.lane != "build"); + rows.push(lane_phase_row("witnesses", "generated-artifact")); + rows.push(lane_phase_row("witnesses", "regen-fixed-point")); + let findings = lane_roster_findings(&rows, Some(RequiredCiLane::Build)); assert!( findings .iter() - .any(|f| f.contains("v2-native") && f.contains("build")), + .any(|f| f.contains("generated-artifact") && f.contains("witnesses")), "the divergence must name the pair: {findings:?}" ); assert!( findings.iter().any(|f| f.contains("owns no phases")), - "the native lane's expectation is now empty and must refuse: {findings:?}" + "the build lane's expectation is now empty and must refuse: {findings:?}" ); } @@ -2361,9 +2356,9 @@ mod tests { fn a_selected_lane_with_no_expected_phases_is_refused() { let rows: Vec<_> = standing_authority_rows() .into_iter() - .filter(|r| r.phase != "v2-native") + .filter(|r| r.lane != "build") .collect(); - let findings = lane_roster_findings(&rows, Some(RequiredCiLane::V2Native)); + let findings = lane_roster_findings(&rows, Some(RequiredCiLane::Build)); assert!(findings.iter().any(|f| f.contains("owns no phases"))); } @@ -2371,10 +2366,15 @@ mod tests { fn the_expected_set_tracks_the_selected_lane() { let rows = standing_authority_rows(); assert_eq!( - expected_lane_phases(&rows, Some(RequiredCiLane::V2Native)), - ["v2-native".to_string()].into_iter().collect() + expected_lane_phases(&rows, Some(RequiredCiLane::Build)), + [ + "generated-artifact".to_string(), + "regen-fixed-point".to_string() + ] + .into_iter() + .collect() ); - assert_eq!(expected_lane_phases(&rows, None).len(), 6); + assert_eq!(expected_lane_phases(&rows, None).len(), 5); } #[test] diff --git a/src/v1/stage0/src/cli_run/required_lane_roster.rs b/src/v1/stage0/src/cli_run/required_lane_roster.rs index 8365dde86df..c4c3fb763df 100644 --- a/src/v1/stage0/src/cli_run/required_lane_roster.rs +++ b/src/v1/stage0/src/cli_run/required_lane_roster.rs @@ -5,11 +5,10 @@ //! is not a declaration. Lane ownership leaves the authority only by evaluation, through //! `gunbc.required_ci_phase_roster` `required_ci_lane_phase_rows`. //! -//! Why this half exists: the required workflow is three independently selected lanes, and the -//! v2-native lane's isolation is load-bearing. A host edit that mapped `V2NativePhase` to another -//! lane while the authority still rosters it in `v2-native` would leave the native job selecting -//! zero phases — `phases_run=0`, no phase failure, a green required job that executed no native -//! test. Joining (phase, lane) pairs in both directions makes that edit red at run start in +//! Why this half exists: the required workflow is independently selected lane jobs. A host edit +//! that mapped a phase to another lane while the authority still rosters it in its own would +//! leave that job selecting zero phases — `phases_run=0`, no phase failure, a green required job +//! that executed nothing. Joining (phase, lane) pairs in both directions makes that edit red at run start in //! every lane; the empty-expectation refusal and the exact ran-set check (both in the bin, over //! the rows this module decodes) make a zero-phase selected lane refuse rather than report //! success over nothing. From 5ea01642773a0a34ac93b4ffe5cd6fc89ee013bd Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 02:50:20 +0000 Subject: [PATCH 15/27] Emit occupancy grouping and cancel expressions through extdeps.github.expressions. Route group and cancel through the modeled AST and one fixture evaluator, keep only the heal != '' gap, and rewrite the main-SHA drop so the subject is required obligations rather than a frozen job roster. --- .../main_per_commit_floor_verdict.dag | 4 +- dag/gunbc/witness/witness_floor_workflow.dag | 296 ++++++++++++++---- docs/design-rung-drops.md | 4 +- 3 files changed, 237 insertions(+), 67 deletions(-) diff --git a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag index babc18eb9ab..53539e527ec 100644 --- a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag +++ b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag @@ -14,7 +14,7 @@ import gunbc.guarantee_rung { MechanicallyPreventable, Mitigatable } data main_per_commit_floor_verdict: RungDrop = RungDrop { identity: "main_per_commit_floor_verdict" as NonEmptyStr, - subject: "every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group", + subject: "every landed main SHA obtains its own terminal verdict for every required acceptance obligation on the workflow", declared: "2026-09-11", @@ -29,6 +29,6 @@ data main_per_commit_floor_verdict: RungDrop = RungDrop { population: [ "push events on refs/heads/main" ], - restoration_trigger: "every superseded pending refs/heads/main SHA receives a terminal adjudication bound to that SHA -- either its affected native subjects execute and pass, or non-affection/coverage by a superseding execution is proved for that SHA. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. P0a.2 is the expected route only if and when it is wired to adjudicate skipped main heads; landing typed affected-set admission that only judges subjects inside a workflow that already executed does not retire this row. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row" + restoration_trigger: "every superseded pending refs/heads/main SHA receives a terminal adjudication, bound to that SHA, of every required acceptance obligation -- executed and passed, or coverage by a superseding execution proved. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row" } } diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index d26c829300e..c73af48728d 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -16,7 +16,7 @@ import gunbc.workflow_capability_closure { workflow_job_capability_closure, capability_closure_is_closed, } -import v2.std.algebra { FreeSemigroup, fold_list, list_append, list_map, non_empty_to_list } +import v2.std.algebra { FreeSemigroup, fold_list, list_append, list_map, non_empty_to_list, list_head, skip, HeadFound, HeadAbsent } import extdeps.languages.yaml.emit { serialize_yaml } import extdeps.languages.yaml.gha_workflow { project_workflow_to_yaml } import v2.workflow.ci_workflow_run_emit { ci_toolchain_homes_isolated_or_refuse_command } @@ -76,7 +76,21 @@ import gunbc.ci_layer_roots { witness_layer_roots } import gunbc.fabric_witness_run { witnesses_lane_run_command, witnesses_lane_measurement_command, build_lane_run_command } import extdeps.exec.command { ArgvCommand, shell_command_render } import gunbc.required_lanes_gate { RequiredLane, required_lanes_gate_stmts } -import extdeps.github.expressions { ContextAccess, Github, Needs, interpolate } +import extdeps.github.expressions { + ContextAccess, + Github, + Needs, + interpolate, + serialize_expression, + default_expression_spellings, + Expression, + FunctionCall, + Format, + StringLiteral, + LogicalOr, + LogicalAnd, + Equals +} import v2.workflow.bash_command_fold_serialize { bash_fold_serialize_program } import v2.workflow.required_ci_measurement_shell { required_ci_measurement_finalize_stmts, @@ -227,8 +241,36 @@ data witness_floor_workflow_name: String = "witnesses" // producer-absent standing yields NeverSupersedeRunning, so cancel-in-progress is literal false. // merge_group and heal revalidation stay immutable and separately keyed (mg-{sha}, // heal-{expected_healed_sha}); other dispatch uses run_id. +fn github_event_name_equals(event: String) -> Expression { + Equals { + left: ContextAccess { context: Github, path: ["event_name"] }, + right: StringLiteral { value: event } + } +} + fn github_event_name_equals_expr(event: String) -> String { - concat(concat("github.event_name == '", event), "'") + serialize_expression(expression: github_event_name_equals(event: event)) +} + +fn gha_format_prefix(prefix: String, arg: Expression) -> Expression { + FunctionCall { + function: Format, + args: [StringLiteral { value: concat(prefix, "{0}") }, arg] + } +} + +fn github_sha_expr() -> Expression { ContextAccess { context: Github, path: ["sha"] } } +fn github_ref_expr() -> Expression { ContextAccess { context: Github, path: ["ref"] } } +fn github_run_id_expr() -> Expression { ContextAccess { context: Github, path: ["run_id"] } } +fn github_pr_number_expr() -> Expression { ContextAccess { context: Github, path: ["event", "pull_request", "number"] } } +fn github_dispatch_input_expr(name: String) -> Expression { ContextAccess { context: Github, path: ["event", "inputs", name] } } + +fn gha_and(left: Expression, right: Expression) -> Expression { + LogicalAnd { left: left, right: right } +} + +fn gha_or(left: Expression, right: Expression) -> Expression { + LogicalOr { left: left, right: right } } type WitnessFloorGroupBranch @@ -238,33 +280,42 @@ type WitnessFloorGroupBranch | RefAfterEvent { event: String } | RunIdFallback { prefix: String } -fn gha_format_arg0(prefix: String, arg: String) -> String { - concat(concat("format('", prefix), concat("{0}', ", concat(arg, ")"))) +// Expression has no NotEquals. GitHub Actions admits `!=`; this is the only string remainder +// in group emission. Dissolves when the grammar carries NotEquals. +data gha_not_equals_empty_gap: String = " != ''" + +fn serialize_gha_or_join(acc: String, next: String) -> String { + if acc == "" { + next + } else { + concat(concat(acc, default_expression_spellings.or_op), next) + } } -fn gha_branch_predicate(branch: WitnessFloorGroupBranch) -> String { +fn serialize_group_branch(branch: WitnessFloorGroupBranch) -> String { match branch { ShaAfterEvent { event: e, prefix: p } => - concat(concat(github_event_name_equals_expr(event: e), " && "), gha_format_arg0(prefix: p, arg: "github.sha")) + serialize_expression(expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_sha_expr()))) NonemptyInputAfterEvent { event: e, input: i, prefix: p } => concat( - concat(github_event_name_equals_expr(event: e), concat(" && github.event.inputs.", concat(i, " != '' && "))), - gha_format_arg0(prefix: p, arg: concat("github.event.inputs.", i)) + serialize_expression(expression: github_event_name_equals(event: e)), + concat( + default_expression_spellings.and_op, + concat( + serialize_expression(expression: github_dispatch_input_expr(name: i)), + concat( + gha_not_equals_empty_gap, + concat(default_expression_spellings.and_op, serialize_expression(expression: gha_format_prefix(prefix: p, arg: github_dispatch_input_expr(name: i)))) + ) + ) + ) ) PrNumberAfterEvent { event: e, prefix: p } => - concat(concat(github_event_name_equals_expr(event: e), " && "), gha_format_arg0(prefix: p, arg: "github.event.pull_request.number")) + serialize_expression(expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_pr_number_expr()))) RefAfterEvent { event: e } => - concat(github_event_name_equals_expr(event: e), " && github.ref") + serialize_expression(expression: gha_and(left: github_event_name_equals(event: e), right: github_ref_expr())) RunIdFallback { prefix: p } => - gha_format_arg0(prefix: p, arg: "github.run_id") - } -} - -fn gha_or_join(acc: String, next: String) -> String { - if acc == "" { - next - } else { - concat(concat(acc, " || "), next) + serialize_expression(expression: gha_format_prefix(prefix: p, arg: github_run_id_expr())) } } @@ -284,14 +335,17 @@ fn witness_floor_group_branches() -> List { fn witness_floor_concurrency_group() -> String { concat( - "witness-floor-${{ ", + "witness-floor-", concat( - fold_list( - xs: witness_floor_group_branches(), - empty: "", - cons: fn(acc, b) { gha_or_join(acc: acc, next: gha_branch_predicate(branch: b)) } - ), - " }}" + default_expression_spellings.interp_open, + concat( + fold_list( + xs: witness_floor_group_branches(), + empty: "", + cons: fn(acc, b) { serialize_gha_or_join(acc: acc, next: serialize_group_branch(branch: b)) } + ), + default_expression_spellings.interp_close + ) ) ) } @@ -310,9 +364,9 @@ type WitnessFloorGroupFixture { dispatch_inputs: List } -type ChosenGroupSuffix - = UnchosenGroupSuffix - | ChosenGroupSuffix { value: String } +fn gha_value_is_truthy(s: String) -> Bool { + s != "" && s != "false" && s != "0" +} fn dispatch_input_value(bindings: List, name: String) -> String { fold_list( @@ -332,49 +386,163 @@ fn dispatch_input_value(bindings: List, name: String) -> S ) } -fn group_branch_matches(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> Bool { - match branch { - ShaAfterEvent { event: e, prefix: _ } => fx.event == e - NonemptyInputAfterEvent { event: e, input: i, prefix: _ } => - fx.event == e && dispatch_input_value(bindings: fx.dispatch_inputs, name: i) != "" - PrNumberAfterEvent { event: e, prefix: _ } => fx.event == e - RefAfterEvent { event: e } => fx.event == e - RunIdFallback { prefix: _ } => true +fn github_path_key(path: List) -> String { + fold_list( + xs: path, + empty: "", + cons: fn(acc, s) { + if acc == "" { + s + } else { + concat(concat(acc, "."), s) + } + } + ) +} + +fn eval_github_inputs_path(path: List, fx: WitnessFloorGroupFixture) -> String { + match list_head(xs: path) { + HeadFound { value: event_seg } => + if event_seg == "event" { + match list_head(xs: skip(xs: path, n: 1)) { + HeadFound { value: inputs_seg } => + if inputs_seg == "inputs" { + match list_head(xs: skip(xs: path, n: 2)) { + HeadFound { value: name } => dispatch_input_value(bindings: fx.dispatch_inputs, name: name) + HeadAbsent => "" + } + } else { + "" + } + HeadAbsent => "" + } + } else { + "" + } + HeadAbsent => "" } } -fn group_branch_suffix(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> String { - match branch { - ShaAfterEvent { event: _, prefix: p } => concat(p, fx.sha) - NonemptyInputAfterEvent { event: _, input: i, prefix: p } => - concat(p, dispatch_input_value(bindings: fx.dispatch_inputs, name: i)) - PrNumberAfterEvent { event: _, prefix: p } => concat(p, fx.pull_request_number) - RefAfterEvent { event: _ } => fx.github_ref - RunIdFallback { prefix: p } => concat(p, fx.run_id) +fn eval_github_path(path: List, fx: WitnessFloorGroupFixture) -> String { + let key = github_path_key(path: path) + if key == "event_name" { + fx.event + } else { + if key == "sha" { + fx.sha + } else { + if key == "ref" { + fx.github_ref + } else { + if key == "run_id" { + fx.run_id + } else { + if key == "event.pull_request.number" { + fx.pull_request_number + } else { + eval_github_inputs_path(path: path, fx: fx) + } + } + } + } + } +} + +fn apply_format_arg0(template: String, arg0: String) -> String { + let n = string_length(s: template) + let m = string_length(s: "{0}") + if n >= m && substring(s: template, start: n - m, end: n) == "{0}" { + concat(substring(s: template, start: 0, end: n - m), arg0) + } else { + "" } } -fn choose_group_suffix(acc: ChosenGroupSuffix, branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> ChosenGroupSuffix { - match acc { - ChosenGroupSuffix { value: v } => ChosenGroupSuffix { value: v } - UnchosenGroupSuffix => - if group_branch_matches(branch: branch, fx: fx) { - ChosenGroupSuffix { value: group_branch_suffix(branch: branch, fx: fx) } +fn eval_format_args(args: List, fx: WitnessFloorGroupFixture) -> String { + match list_head(xs: args) { + HeadFound { value: tmpl } => + match tmpl { + StringLiteral { value: t } => + match list_head(xs: skip(xs: args, n: 1)) { + HeadFound { value: arg } => apply_format_arg0(template: t, arg0: eval_gha_expression(expression: arg, fx: fx)) + HeadAbsent => "" + } + ContextAccess { context: _, path: _ } => "" + FunctionCall { function: _, args: _ } => "" + LogicalOr { left: _, right: _ } => "" + LogicalAnd { left: _, right: _ } => "" + Equals { left: _, right: _ } => "" + } + HeadAbsent => "" + } +} + +fn eval_gha_expression(expression: Expression, fx: WitnessFloorGroupFixture) -> String { + match expression { + ContextAccess { context: Github, path: p } => eval_github_path(path: p, fx: fx) + ContextAccess { context: _, path: _ } => "" + FunctionCall { function: Format, args: a } => eval_format_args(args: a, fx: fx) + FunctionCall { function: _, args: _ } => "" + StringLiteral { value: v } => v + LogicalOr { left: l, right: r } => + if gha_value_is_truthy(s: eval_gha_expression(expression: l, fx: fx)) { + eval_gha_expression(expression: l, fx: fx) } else { - UnchosenGroupSuffix + eval_gha_expression(expression: r, fx: fx) + } + LogicalAnd { left: l, right: r } => + if gha_value_is_truthy(s: eval_gha_expression(expression: l, fx: fx)) { + eval_gha_expression(expression: r, fx: fx) + } else { + eval_gha_expression(expression: l, fx: fx) + } + Equals { left: l, right: r } => + if eval_gha_expression(expression: l, fx: fx) == eval_gha_expression(expression: r, fx: fx) { + "true" + } else { + "" } } } +fn eval_group_branch(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> String { + match branch { + ShaAfterEvent { event: e, prefix: p } => + eval_gha_expression( + expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_sha_expr())), + fx: fx + ) + NonemptyInputAfterEvent { event: e, input: i, prefix: p } => + if gha_value_is_truthy(s: eval_gha_expression(expression: github_event_name_equals(event: e), fx: fx)) + && dispatch_input_value(bindings: fx.dispatch_inputs, name: i) != "" { + eval_gha_expression(expression: gha_format_prefix(prefix: p, arg: github_dispatch_input_expr(name: i)), fx: fx) + } else { + "" + } + PrNumberAfterEvent { event: e, prefix: p } => + eval_gha_expression( + expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_pr_number_expr())), + fx: fx + ) + RefAfterEvent { event: e } => + eval_gha_expression(expression: gha_and(left: github_event_name_equals(event: e), right: github_ref_expr()), fx: fx) + RunIdFallback { prefix: p } => + eval_gha_expression(expression: gha_format_prefix(prefix: p, arg: github_run_id_expr()), fx: fx) + } +} + fn resolve_witness_floor_group_suffix(fx: WitnessFloorGroupFixture) -> String { - match fold_list( + fold_list( xs: witness_floor_group_branches(), - empty: UnchosenGroupSuffix, - cons: fn(acc, b) { choose_group_suffix(acc: acc, branch: b, fx: fx) } - ) { - ChosenGroupSuffix { value: v } => v - UnchosenGroupSuffix => "" - } + empty: "", + cons: fn(acc, b) { + if gha_value_is_truthy(s: acc) { + acc + } else { + eval_group_branch(branch: b, fx: fx) + } + } + ) } fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> String { @@ -491,9 +659,11 @@ fn is_mutable_ci_head_event(event: String) -> Bool { } fn mutable_head_runs_cancel_expression() -> String { - concat( - concat("${{ github.event_name == '", github_event_name_pull_request), - concat("' || github.event_name == '", concat(github_event_name_push, "' }}")) + interpolate( + expression: gha_or( + left: github_event_name_equals(event: github_event_name_pull_request), + right: github_event_name_equals(event: github_event_name_push) + ) ) } diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index 4381b54aec8..b4205d13f16 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -210,6 +210,6 @@ admission rows on falsifier-family cadences with no scheduled route, awaiting tr The v2 native route (emitted-native compiler over the derived v2.test.* universe) as a required CI lane: RUNG DROP, mechanically preventable -> mitigatable (deleted without replacement). Population: gunbc.witness_v2_native_route native_route_admission — every receipt clause, on every merge candidate, the derived v2.test.* universe executed by the emitted-native compiler (positive population), v2.native_lane_fixture.control native_lane_false_control / native_lane_true_control (live-verdict controls), fixtures/native_lane_malformed/poison.dag.poisoned (malformed-specimen control), the old-route-withdrawn (no-fallback) control. Restored when: A REQUIRED NATIVE-ROUTE JOB WHOSE WALL FITS THE ACCEPTANCE PATH, designed from the ground up against an operator-agreed contract and not re-added into the same envelope: the job must conclude on every merge candidate inside its declared timeout on the real runner class (a measured wall, not a cited one), a newer head of the same subject must supersede the older run so no head holds more than one native claimant, and its red must still discriminate every clause of native_route_admission — universe join, positive population, classified refusals, per-identity agreement with the floor reference, and all four controls. Whether that is reached by affected-set admission that runs only the universe a change touches, by a native run cheap enough to fit whole, or by a job that runs on a different cadence with its own row, is the design decision this drop waits on; the first two retire this row, the third replaces it with a differently-named drop. Re-adding `required-v2-native` with its 2026-09-10 shape does not retire this row and is refused by test.claim.witness_floor_workflow_consolidation_witness_test w_RED_the_deleted_lanes_do_not_return. -### every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group — declared 2026-09-11 +### every landed main SHA obtains its own terminal verdict for every required acceptance obligation on the workflow — declared 2026-09-11 -every main SHA obtains its own floor/build/native verdict via a unique github.run_id concurrency group: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: every superseded pending refs/heads/main SHA receives a terminal adjudication bound to that SHA -- either its affected native subjects execute and pass, or non-affection/coverage by a superseding execution is proved for that SHA. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. P0a.2 is the expected route only if and when it is wired to adjudicate skipped main heads; landing typed affected-set admission that only judges subjects inside a workflow that already executed does not retire this row. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row. +every landed main SHA obtains its own terminal verdict for every required acceptance obligation on the workflow: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: every superseded pending refs/heads/main SHA receives a terminal adjudication, bound to that SHA, of every required acceptance obligation -- executed and passed, or coverage by a superseding execution proved. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row. From 52909e76ccef00c3f15fbe5c4e181938393707b5 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 03:15:16 +0000 Subject: [PATCH 16/27] Model NotEquals in GitHub expressions and drop the heal gap row. The occupancy group is one interpolated AST; the fixture evaluator walks it, including !=, so heal nonempty-input is no longer a second string path. --- dag/extdeps/github/expressions.dag | 28 ++-- dag/gunbc/witness/witness_floor_workflow.dag | 121 ++++++------------ .../claim/gha_expression_roundtrip_test.dag | 12 ++ ...or_workflow_consolidation_witness_test.dag | 11 ++ 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/dag/extdeps/github/expressions.dag b/dag/extdeps/github/expressions.dag index 13f5d036c75..b4d0875fb24 100644 --- a/dag/extdeps/github/expressions.dag +++ b/dag/extdeps/github/expressions.dag @@ -46,6 +46,7 @@ type Expression | LogicalOr { left: Expression, right: Expression } | LogicalAnd { left: Expression, right: Expression } | Equals { left: Expression, right: Expression } + | NotEquals { left: Expression, right: Expression } type TemplateSegment = LiteralText { text: String } @@ -98,6 +99,7 @@ type ExpressionSpellings { or_op: String and_op: String eq_op: String + neq_op: String interp_open: String interp_close: String index_open: String @@ -113,6 +115,7 @@ data default_expression_spellings: ExpressionSpellings = { or_op: " || ", and_op: " && ", eq_op: " == ", + neq_op: " != ", interp_open: "${{ ", interp_close: " }}", index_open: "['", @@ -207,6 +210,8 @@ fn serialize_expression_with(expression: Expression, sp: ExpressionSpellings) -> join([serialize_expression_with(expression: l, sp: sp), serialize_expression_with(expression: r, sp: sp)], sp.and_op) Equals { left: l, right: r } => join([serialize_expression_with(expression: l, sp: sp), serialize_expression_with(expression: r, sp: sp)], sp.eq_op) + NotEquals { left: l, right: r } => + join([serialize_expression_with(expression: l, sp: sp), serialize_expression_with(expression: r, sp: sp)], sp.neq_op) } } @@ -451,6 +456,9 @@ fn expression_accesses_runner_var_via_env(expression: Expression) -> Bool { Equals { left: l, right: r } => expression_accesses_runner_var_via_env(expression: l) || expression_accesses_runner_var_via_env(expression: r) + NotEquals { left: l, right: r } => + expression_accesses_runner_var_via_env(expression: l) || + expression_accesses_runner_var_via_env(expression: r) StringLiteral { value: _ } => false } } @@ -469,6 +477,9 @@ fn expression_uses_context(expression: Expression, target: ExpressionContext) -> Equals { left: l, right: r } => expression_uses_context(expression: l, target: target) || expression_uses_context(expression: r, target: target) + NotEquals { left: l, right: r } => + expression_uses_context(expression: l, target: target) || + expression_uses_context(expression: r, target: target) StringLiteral { value: _ } => false } } @@ -513,16 +524,17 @@ fn template_uses_context(template: ExpressionTemplate, target: ExpressionContext // to BOTH directions plus the goldens that pin them, not a serializer edit. // // DECLARED ASYMMETRY, NAMED RATHER THAN LEFT IMPLIED (review 49460): parse_expression_with reads -// Or, calls, context accesses and string literals; it does NOT read && or ==. So these two +// Or, calls, context accesses and string literals; it does NOT read &&, ==, or !=. So these // operators are EMISSION-ONLY today, and this module is bidirectional for the constructs that // predate them and one-way for these. That is a real §4 asymmetry, admissible here only because no -// consumer reads these forms BACK — the sole use is lowering a job condition that GitHub evaluates -// and nothing in this tree re-ingests. It stops being admissible the moment any consumer parses a -// condition containing && or ==, which is the trigger: ingest arms for both operators plus -// round-trip witnesses covering them land before that consumer, not after it. Stated explicitly -// because the previous revision of this row broke bidirectionality by accident and shipped it; an -// asymmetry that is declared and triggered is a frontier, one that is merely true is a defect -// waiting to be discovered. +// consumer reads these forms BACK — the uses are lowering a job condition and a concurrency group +// that GitHub evaluates and nothing in this tree re-ingests. It stops being admissible the moment +// any consumer parses a condition containing &&, ==, or !=, which is the trigger: ingest arms for +// those operators plus round-trip witnesses covering them land before that consumer, not after +// it. Stated explicitly because the previous revision of this row broke bidirectionality by +// accident and shipped it; an asymmetry that is declared and triggered is a frontier, one that is +// merely true is a defect waiting to be discovered. NotEquals joins that emission-only set; ingest +// is not extended here. fn any_template_accesses_runner_var_via_env(templates: List) -> Bool { fold(templates, init: false, f: (acc, template) => diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index c73af48728d..92a68dc7f4a 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -89,7 +89,8 @@ import extdeps.github.expressions { StringLiteral, LogicalOr, LogicalAnd, - Equals + Equals, + NotEquals } import v2.workflow.bash_command_fold_serialize { bash_fold_serialize_program } import v2.workflow.required_ci_measurement_shell { @@ -280,45 +281,51 @@ type WitnessFloorGroupBranch | RefAfterEvent { event: String } | RunIdFallback { prefix: String } -// Expression has no NotEquals. GitHub Actions admits `!=`; this is the only string remainder -// in group emission. Dissolves when the grammar carries NotEquals. -data gha_not_equals_empty_gap: String = " != ''" +fn gha_not_equals(left: Expression, right: Expression) -> Expression { + NotEquals { left: left, right: right } +} -fn serialize_gha_or_join(acc: String, next: String) -> String { - if acc == "" { - next - } else { - concat(concat(acc, default_expression_spellings.or_op), next) - } +fn nonempty_input_after_event_expr(event: String, input: String, prefix: String) -> Expression { + gha_and( + left: github_event_name_equals(event: event), + right: gha_and( + left: gha_not_equals(left: github_dispatch_input_expr(name: input), right: StringLiteral { value: "" }), + right: gha_format_prefix(prefix: prefix, arg: github_dispatch_input_expr(name: input)) + ) + ) } -fn serialize_group_branch(branch: WitnessFloorGroupBranch) -> String { +fn group_branch_expr(branch: WitnessFloorGroupBranch) -> Expression { match branch { ShaAfterEvent { event: e, prefix: p } => - serialize_expression(expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_sha_expr()))) + gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_sha_expr())) NonemptyInputAfterEvent { event: e, input: i, prefix: p } => - concat( - serialize_expression(expression: github_event_name_equals(event: e)), - concat( - default_expression_spellings.and_op, - concat( - serialize_expression(expression: github_dispatch_input_expr(name: i)), - concat( - gha_not_equals_empty_gap, - concat(default_expression_spellings.and_op, serialize_expression(expression: gha_format_prefix(prefix: p, arg: github_dispatch_input_expr(name: i)))) - ) - ) - ) - ) + nonempty_input_after_event_expr(event: e, input: i, prefix: p) PrNumberAfterEvent { event: e, prefix: p } => - serialize_expression(expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_pr_number_expr()))) + gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_pr_number_expr())) RefAfterEvent { event: e } => - serialize_expression(expression: gha_and(left: github_event_name_equals(event: e), right: github_ref_expr())) + gha_and(left: github_event_name_equals(event: e), right: github_ref_expr()) RunIdFallback { prefix: p } => - serialize_expression(expression: gha_format_prefix(prefix: p, arg: github_run_id_expr())) + gha_format_prefix(prefix: p, arg: github_run_id_expr()) } } +fn witness_floor_group_expression() -> Expression { + match list_head(xs: witness_floor_group_branches()) { + HeadAbsent => gha_format_prefix(prefix: "run-", arg: github_run_id_expr()) + HeadFound { value: first } => + fold_list( + xs: skip(xs: witness_floor_group_branches(), n: 1), + empty: group_branch_expr(branch: first), + cons: fn(acc, b) { gha_or(left: acc, right: group_branch_expr(branch: b)) } + ) + } +} + +fn witness_floor_concurrency_group() -> String { + concat("witness-floor-", interpolate(expression: witness_floor_group_expression())) +} + fn witness_floor_group_branches() -> List { [ ShaAfterEvent { event: github_event_name_merge_group, prefix: "mg-" }, @@ -333,23 +340,6 @@ fn witness_floor_group_branches() -> List { ] } -fn witness_floor_concurrency_group() -> String { - concat( - "witness-floor-", - concat( - default_expression_spellings.interp_open, - concat( - fold_list( - xs: witness_floor_group_branches(), - empty: "", - cons: fn(acc, b) { serialize_gha_or_join(acc: acc, next: serialize_group_branch(branch: b)) } - ), - default_expression_spellings.interp_close - ) - ) - ) -} - type DispatchInputBinding { name: String value: String @@ -472,6 +462,7 @@ fn eval_format_args(args: List, fx: WitnessFloorGroupFixture) -> Str LogicalOr { left: _, right: _ } => "" LogicalAnd { left: _, right: _ } => "" Equals { left: _, right: _ } => "" + NotEquals { left: _, right: _ } => "" } HeadAbsent => "" } @@ -502,47 +493,17 @@ fn eval_gha_expression(expression: Expression, fx: WitnessFloorGroupFixture) -> } else { "" } - } -} - -fn eval_group_branch(branch: WitnessFloorGroupBranch, fx: WitnessFloorGroupFixture) -> String { - match branch { - ShaAfterEvent { event: e, prefix: p } => - eval_gha_expression( - expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_sha_expr())), - fx: fx - ) - NonemptyInputAfterEvent { event: e, input: i, prefix: p } => - if gha_value_is_truthy(s: eval_gha_expression(expression: github_event_name_equals(event: e), fx: fx)) - && dispatch_input_value(bindings: fx.dispatch_inputs, name: i) != "" { - eval_gha_expression(expression: gha_format_prefix(prefix: p, arg: github_dispatch_input_expr(name: i)), fx: fx) - } else { + NotEquals { left: l, right: r } => + if eval_gha_expression(expression: l, fx: fx) == eval_gha_expression(expression: r, fx: fx) { "" + } else { + "true" } - PrNumberAfterEvent { event: e, prefix: p } => - eval_gha_expression( - expression: gha_and(left: github_event_name_equals(event: e), right: gha_format_prefix(prefix: p, arg: github_pr_number_expr())), - fx: fx - ) - RefAfterEvent { event: e } => - eval_gha_expression(expression: gha_and(left: github_event_name_equals(event: e), right: github_ref_expr()), fx: fx) - RunIdFallback { prefix: p } => - eval_gha_expression(expression: gha_format_prefix(prefix: p, arg: github_run_id_expr()), fx: fx) } } fn resolve_witness_floor_group_suffix(fx: WitnessFloorGroupFixture) -> String { - fold_list( - xs: witness_floor_group_branches(), - empty: "", - cons: fn(acc, b) { - if gha_value_is_truthy(s: acc) { - acc - } else { - eval_group_branch(branch: b, fx: fx) - } - } - ) + eval_gha_expression(expression: witness_floor_group_expression(), fx: fx) } fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> String { diff --git a/dag/test/claim/gha_expression_roundtrip_test.dag b/dag/test/claim/gha_expression_roundtrip_test.dag index 2655897c772..b947f400733 100644 --- a/dag/test/claim/gha_expression_roundtrip_test.dag +++ b/dag/test/claim/gha_expression_roundtrip_test.dag @@ -92,6 +92,7 @@ fn override_spellings(path_sep: String, or_op: String) -> ExpressionSpellings { or_op: or_op, and_op: default_expression_spellings.and_op, eq_op: default_expression_spellings.eq_op, + neq_op: default_expression_spellings.neq_op, interp_open: default_expression_spellings.interp_open, interp_close: default_expression_spellings.interp_close, index_open: default_expression_spellings.index_open, @@ -221,3 +222,14 @@ test fn witness_emit_non_regression() -> Bool { && (serialize_template(template: t) == "cargo-ci-${{ runner.os }}-${{ runner.arch }}") } +fn not_equals_empty_input_expr() -> Expression { + NotEquals { + left: ContextAccess { context: Github, path: ["event", "inputs", "expected_healed_sha"] }, + right: StringLiteral { value: "" } + } +} + +test fn witness_not_equals_renders_ne_operator() -> Bool { + serialize_expression(expression: not_equals_empty_input_expr()) == "github.event.inputs.expected_healed_sha != ''" +} + diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 38f041806a0..4422b7e43fc 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -493,6 +493,16 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc github_ref: "refs/heads/main", dispatch_inputs: [] } + let heal_empty = WitnessFloorGroupFixture { + event: github_event_name_workflow_dispatch, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + github_ref: "refs/heads/main", + dispatch_inputs: [ + DispatchInputBinding { name: ci_heal_expected_healed_sha_input_name, value: "" } + ] + } let pr = WitnessFloorGroupFixture { event: github_event_name_pull_request, sha: "mgsha", @@ -511,6 +521,7 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc } resolve_witness_floor_concurrency_group(fx: mg) == "witness-floor-mg-mgsha" && resolve_witness_floor_concurrency_group(fx: heal) == "witness-floor-heal-healsha" + && resolve_witness_floor_concurrency_group(fx: heal_empty) == "witness-floor-run-rid" && resolve_witness_floor_concurrency_group(fx: bare_dispatch) == "witness-floor-run-rid" && resolve_witness_floor_concurrency_group(fx: pr) == "witness-floor-pr-7" && resolve_witness_floor_concurrency_group(fx: push) == "witness-floor-refs/heads/main" From bb591f30e0bea65bb284eea966c8f50f6c8b1ccb Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 03:26:31 +0000 Subject: [PATCH 17/27] Delete the producer-side event-name string serializer. The occupancy group is already one interpolated AST; tests now spell expected YAML from event-name constants instead of a second serializer in the workflow module. --- dag/gunbc/witness/witness_floor_workflow.dag | 6 ------ ...or_workflow_consolidation_witness_test.dag | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 92a68dc7f4a..0fb6d55f8bf 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -81,8 +81,6 @@ import extdeps.github.expressions { Github, Needs, interpolate, - serialize_expression, - default_expression_spellings, Expression, FunctionCall, Format, @@ -249,10 +247,6 @@ fn github_event_name_equals(event: String) -> Expression { } } -fn github_event_name_equals_expr(event: String) -> String { - serialize_expression(expression: github_event_name_equals(event: event)) -} - fn gha_format_prefix(prefix: String, arg: Expression) -> Expression { FunctionCall { function: Format, diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 4422b7e43fc..a38cd753cde 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, github_event_name_equals_expr, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } import gunbc.fleet_converge_workflow { fleet_converge_workflow } @@ -530,32 +530,35 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc WitnessFloorGenerated { content: yml } => string_contains( s: yml, - pattern: concat(github_event_name_equals_expr(event: github_event_name_merge_group), " && format('mg-{0}', github.sha)") + pattern: concat("github.event_name == '", concat(github_event_name_merge_group, "' && format('mg-{0}', github.sha)")) ) && string_contains( s: yml, pattern: concat( - github_event_name_equals_expr(event: github_event_name_workflow_dispatch), + "github.event_name == '", concat( - " && github.event.inputs.", + github_event_name_workflow_dispatch, concat( - ci_heal_expected_healed_sha_input_name, - concat(" != '' && format('heal-{0}', github.event.inputs.", concat(ci_heal_expected_healed_sha_input_name, ")")) + "' && github.event.inputs.", + concat( + ci_heal_expected_healed_sha_input_name, + concat(" != '' && format('heal-{0}', github.event.inputs.", concat(ci_heal_expected_healed_sha_input_name, ")")) + ) ) ) ) ) && string_contains( s: yml, - pattern: concat(github_event_name_equals_expr(event: github_event_name_pull_request), " && format('pr-{0}', github.event.pull_request.number)") + pattern: concat("github.event_name == '", concat(github_event_name_pull_request, "' && format('pr-{0}', github.event.pull_request.number)")) ) && string_contains( s: yml, - pattern: concat(github_event_name_equals_expr(event: github_event_name_push), " && github.ref") + pattern: concat("github.event_name == '", concat(github_event_name_push, "' && github.ref")) ) && !string_contains( s: yml, - pattern: concat(github_event_name_equals_expr(event: github_event_name_push), " && format('pr-{0}'") + pattern: concat("github.event_name == '", concat(github_event_name_push, "' && format('pr-{0}'")) ) && string_contains(s: yml, pattern: "cancel-in-progress: false") && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) From b80c7c5e4575220fc1b1a4f539989eeaefa17e8c Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 04:01:48 +0000 Subject: [PATCH 18/27] Move GHA evaluation into extdeps.github.expressions and refuse unmodeled forms. The fixture resolver consumes the cited evaluator; empty is no longer the encoding of an unknown path or format arity, so those cases go red instead of collapsing to the run-id fallback. --- dag/extdeps/github/expressions.dag | 184 ++++++++++++++++-- dag/gunbc/witness/witness_floor_workflow.dag | 150 ++++---------- .../claim/gha_expression_roundtrip_test.dag | 21 ++ ...or_workflow_consolidation_witness_test.dag | 66 ++++++- 4 files changed, 287 insertions(+), 134 deletions(-) diff --git a/dag/extdeps/github/expressions.dag b/dag/extdeps/github/expressions.dag index b4d0875fb24..f6941db1116 100644 --- a/dag/extdeps/github/expressions.dag +++ b/dag/extdeps/github/expressions.dag @@ -3,6 +3,8 @@ module extdeps.github.expressions import extdeps.external_authority { ExternalAuthority } import extdeps.uri { Uri, Https } import extdeps.communication.medium { Lossless } +import v2.std.algebra { fold_list, list_head, skip, HeadFound, HeadAbsent, length } +import std.types { Bool, List, String, Int } data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { uri: Uri { @@ -11,6 +13,20 @@ data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority { } } +data github_expression_operators_authority: ExternalAuthority = ExternalAuthority { + uri: Uri { + scheme: Https + locator: "docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#operators" + } +} + +data github_expression_format_authority: ExternalAuthority = ExternalAuthority { + uri: Uri { + scheme: Https + locator: "docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#format" + } +} + type ExpressionContext = Github | Env @@ -524,19 +540,165 @@ fn template_uses_context(template: ExpressionTemplate, target: ExpressionContext // to BOTH directions plus the goldens that pin them, not a serializer edit. // // DECLARED ASYMMETRY, NAMED RATHER THAN LEFT IMPLIED (review 49460): parse_expression_with reads -// Or, calls, context accesses and string literals; it does NOT read &&, ==, or !=. So these -// operators are EMISSION-ONLY today, and this module is bidirectional for the constructs that -// predate them and one-way for these. That is a real §4 asymmetry, admissible here only because no -// consumer reads these forms BACK — the uses are lowering a job condition and a concurrency group -// that GitHub evaluates and nothing in this tree re-ingests. It stops being admissible the moment -// any consumer parses a condition containing &&, ==, or !=, which is the trigger: ingest arms for -// those operators plus round-trip witnesses covering them land before that consumer, not after -// it. Stated explicitly because the previous revision of this row broke bidirectionality by -// accident and shipped it; an asymmetry that is declared and triggered is a frontier, one that is -// merely true is a defect waiting to be discovered. NotEquals joins that emission-only set; ingest -// is not extended here. +// Or, calls, context accesses and string literals; it does NOT read &&, ==, or !=. So those +// operators remain UNPARSED from surface syntax. That is a real §4 ingest asymmetry. A +// structural evaluator over an already-constructed Expression AST is not that trigger: it +// does not ingest operator text, so it does not fire the "ingest arms plus round-trip witnesses +// land before a parse consumer" row. The trigger remains a consumer that parses &&, ==, or != +// from a string. This PR's concurrency-group fixture resolver is the first in-tree consumer +// that reads the AST back by evaluation; it is not a re-ingestion of serialized operator +// spellings. NotEquals is emitted and evaluated; it is still not ingested from text. fn any_template_accesses_runner_var_via_env(templates: List) -> Bool { fold(templates, init: false, f: (acc, template) => acc || template_accesses_runner_var_via_env(template: template)) } + +// GITHUB ACTIONS TRUTHINESS (github_expression_operators_authority, operators): false, 0, and +// empty string are falsy. This evaluator's string encoding of a Boolean false is the empty +// string; "false" and "0" are also falsy. format() substitution is github_expression_format_authority +// `format` with replaceValue0 as `{0}`. +fn gha_value_is_truthy(s: String) -> Bool { + s != "" && s != "false" && s != "0" +} + +fn github_path_key(path: List) -> String { + fold_list( + xs: path, + empty: "", + cons: fn(acc, s) { + if acc == "" { + s + } else { + concat(concat(acc, "."), s) + } + } + ) +} + +type GithubPathResolution + = GithubPathValue { value: String } + | GithubPathUnmodeled + +type FormatEvaluationRefusal + = FormatTemplateNotLiteral + | FormatArityNotOneSubstitution { argument_count: Int } + | FormatPlaceholderNotArg0 { template: String } + +type ExpressionEvaluationRefusal + = UnmodeledContext { context: ExpressionContext, path: List } + | UnmodeledFunction { function: ExpressionFunction } + | UnmodeledFormat { cause: FormatEvaluationRefusal } + +type ExpressionEvaluation + = ExpressionValue { value: String } + | ExpressionEvaluationRefused { cause: ExpressionEvaluationRefusal } + +fn apply_format_arg0(template: String, arg0: String) -> ExpressionEvaluation { + let n = string_length(s: template) + let m = string_length(s: "{0}") + if string_contains(template, "{1}") || !(n >= m && substring(s: template, start: n - m, end: n) == "{0}") { + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatPlaceholderNotArg0 { template: template } } } + } else { + ExpressionValue { value: concat(substring(s: template, start: 0, end: n - m), arg0) } + } +} + +fn eval_format_args(args: List, github_path: fn(List) -> GithubPathResolution) -> ExpressionEvaluation { + let n = length(xs: args) + if n != 2 { + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatArityNotOneSubstitution { argument_count: n } } } + } else { + match list_head(xs: args) { + HeadAbsent => ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatArityNotOneSubstitution { argument_count: 0 } } } + HeadFound { value: tmpl } => + match tmpl { + StringLiteral { value: t } => + match list_head(xs: skip(xs: args, n: 1)) { + HeadAbsent => ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatArityNotOneSubstitution { argument_count: n } } } + HeadFound { value: arg } => + match evaluate_expression(expression: arg, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: arg0 } => apply_format_arg0(template: t, arg0: arg0) + } + } + ContextAccess { context: _, path: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatTemplateNotLiteral } } + FunctionCall { function: _, args: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatTemplateNotLiteral } } + LogicalOr { left: _, right: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatTemplateNotLiteral } } + LogicalAnd { left: _, right: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatTemplateNotLiteral } } + Equals { left: _, right: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatTemplateNotLiteral } } + NotEquals { left: _, right: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatTemplateNotLiteral } } + } + } + } +} + +fn evaluate_expression(expression: Expression, github_path: fn(List) -> GithubPathResolution) -> ExpressionEvaluation { + match expression { + ContextAccess { context: Github, path: p } => + match github_path(p) { + GithubPathValue { value: v } => ExpressionValue { value: v } + GithubPathUnmodeled => ExpressionEvaluationRefused { cause: UnmodeledContext { context: Github, path: p } } + } + ContextAccess { context: c, path: p } => + ExpressionEvaluationRefused { cause: UnmodeledContext { context: c, path: p } } + FunctionCall { function: Format, args: a } => eval_format_args(args: a, github_path: github_path) + FunctionCall { function: f, args: _ } => + ExpressionEvaluationRefused { cause: UnmodeledFunction { function: f } } + StringLiteral { value: v } => ExpressionValue { value: v } + LogicalOr { left: l, right: r } => + match evaluate_expression(expression: l, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: lv } => + if gha_value_is_truthy(s: lv) { + ExpressionValue { value: lv } + } else { + evaluate_expression(expression: r, github_path: github_path) + } + } + LogicalAnd { left: l, right: r } => + match evaluate_expression(expression: l, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: lv } => + if gha_value_is_truthy(s: lv) { + evaluate_expression(expression: r, github_path: github_path) + } else { + ExpressionValue { value: lv } + } + } + Equals { left: l, right: r } => + match evaluate_expression(expression: l, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: lv } => + match evaluate_expression(expression: r, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: rv } => + if lv == rv { + ExpressionValue { value: "true" } + } else { + ExpressionValue { value: "" } + } + } + } + NotEquals { left: l, right: r } => + match evaluate_expression(expression: l, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: lv } => + match evaluate_expression(expression: r, github_path: github_path) { + ExpressionEvaluationRefused { cause: c } => ExpressionEvaluationRefused { cause: c } + ExpressionValue { value: rv } => + if lv == rv { + ExpressionValue { value: "" } + } else { + ExpressionValue { value: "true" } + } + } + } + } +} diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 0fb6d55f8bf..0a779f219e5 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -88,7 +88,16 @@ import extdeps.github.expressions { LogicalOr, LogicalAnd, Equals, - NotEquals + NotEquals, + evaluate_expression, + github_path_key, + GithubPathValue, + GithubPathUnmodeled, + GithubPathResolution, + ExpressionValue, + ExpressionEvaluationRefused, + ExpressionEvaluation, + ExpressionEvaluationRefusal } import v2.workflow.bash_command_fold_serialize { bash_fold_serialize_program } import v2.workflow.required_ci_measurement_shell { @@ -348,10 +357,6 @@ type WitnessFloorGroupFixture { dispatch_inputs: List } -fn gha_value_is_truthy(s: String) -> Bool { - s != "" && s != "false" && s != "0" -} - fn dispatch_input_value(bindings: List, name: String) -> String { fold_list( xs: bindings, @@ -370,61 +375,33 @@ fn dispatch_input_value(bindings: List, name: String) -> S ) } -fn github_path_key(path: List) -> String { - fold_list( - xs: path, - empty: "", - cons: fn(acc, s) { - if acc == "" { - s - } else { - concat(concat(acc, "."), s) - } - } - ) -} - -fn eval_github_inputs_path(path: List, fx: WitnessFloorGroupFixture) -> String { - match list_head(xs: path) { - HeadFound { value: event_seg } => - if event_seg == "event" { - match list_head(xs: skip(xs: path, n: 1)) { - HeadFound { value: inputs_seg } => - if inputs_seg == "inputs" { - match list_head(xs: skip(xs: path, n: 2)) { - HeadFound { value: name } => dispatch_input_value(bindings: fx.dispatch_inputs, name: name) - HeadAbsent => "" - } - } else { - "" - } - HeadAbsent => "" - } - } else { - "" - } - HeadAbsent => "" - } -} - -fn eval_github_path(path: List, fx: WitnessFloorGroupFixture) -> String { +fn fixture_github_path(fx: WitnessFloorGroupFixture, path: List) -> GithubPathResolution { let key = github_path_key(path: path) if key == "event_name" { - fx.event + GithubPathValue { value: fx.event } } else { if key == "sha" { - fx.sha + GithubPathValue { value: fx.sha } } else { if key == "ref" { - fx.github_ref + GithubPathValue { value: fx.github_ref } } else { if key == "run_id" { - fx.run_id + GithubPathValue { value: fx.run_id } } else { if key == "event.pull_request.number" { - fx.pull_request_number + GithubPathValue { value: fx.pull_request_number } } else { - eval_github_inputs_path(path: path, fx: fx) + if starts_with(s: key, prefix: "event.inputs.") { + GithubPathValue { + value: dispatch_input_value( + bindings: fx.dispatch_inputs, + name: substring(s: key, start: string_length(s: "event.inputs."), end: string_length(s: key)) + ) + } + } else { + GithubPathUnmodeled + } } } } @@ -432,78 +409,21 @@ fn eval_github_path(path: List, fx: WitnessFloorGroupFixture) -> String } } -fn apply_format_arg0(template: String, arg0: String) -> String { - let n = string_length(s: template) - let m = string_length(s: "{0}") - if n >= m && substring(s: template, start: n - m, end: n) == "{0}" { - concat(substring(s: template, start: 0, end: n - m), arg0) - } else { - "" - } +fn evaluate_against_group_fixture(expression: Expression, fx: WitnessFloorGroupFixture) -> ExpressionEvaluation { + evaluate_expression(expression: expression, github_path: fn(path) { fixture_github_path(fx: fx, path: path) }) } -fn eval_format_args(args: List, fx: WitnessFloorGroupFixture) -> String { - match list_head(xs: args) { - HeadFound { value: tmpl } => - match tmpl { - StringLiteral { value: t } => - match list_head(xs: skip(xs: args, n: 1)) { - HeadFound { value: arg } => apply_format_arg0(template: t, arg0: eval_gha_expression(expression: arg, fx: fx)) - HeadAbsent => "" - } - ContextAccess { context: _, path: _ } => "" - FunctionCall { function: _, args: _ } => "" - LogicalOr { left: _, right: _ } => "" - LogicalAnd { left: _, right: _ } => "" - Equals { left: _, right: _ } => "" - NotEquals { left: _, right: _ } => "" - } - HeadAbsent => "" - } -} +type WitnessFloorGroupResolution + = WitnessFloorGroupResolved { group: String } + | WitnessFloorGroupRefused { cause: ExpressionEvaluationRefusal } -fn eval_gha_expression(expression: Expression, fx: WitnessFloorGroupFixture) -> String { - match expression { - ContextAccess { context: Github, path: p } => eval_github_path(path: p, fx: fx) - ContextAccess { context: _, path: _ } => "" - FunctionCall { function: Format, args: a } => eval_format_args(args: a, fx: fx) - FunctionCall { function: _, args: _ } => "" - StringLiteral { value: v } => v - LogicalOr { left: l, right: r } => - if gha_value_is_truthy(s: eval_gha_expression(expression: l, fx: fx)) { - eval_gha_expression(expression: l, fx: fx) - } else { - eval_gha_expression(expression: r, fx: fx) - } - LogicalAnd { left: l, right: r } => - if gha_value_is_truthy(s: eval_gha_expression(expression: l, fx: fx)) { - eval_gha_expression(expression: r, fx: fx) - } else { - eval_gha_expression(expression: l, fx: fx) - } - Equals { left: l, right: r } => - if eval_gha_expression(expression: l, fx: fx) == eval_gha_expression(expression: r, fx: fx) { - "true" - } else { - "" - } - NotEquals { left: l, right: r } => - if eval_gha_expression(expression: l, fx: fx) == eval_gha_expression(expression: r, fx: fx) { - "" - } else { - "true" - } +fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> WitnessFloorGroupResolution { + match evaluate_against_group_fixture(expression: witness_floor_group_expression(), fx: fx) { + ExpressionValue { value: suffix } => WitnessFloorGroupResolved { group: concat("witness-floor-", suffix) } + ExpressionEvaluationRefused { cause: c } => WitnessFloorGroupRefused { cause: c } } } -fn resolve_witness_floor_group_suffix(fx: WitnessFloorGroupFixture) -> String { - eval_gha_expression(expression: witness_floor_group_expression(), fx: fx) -} - -fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> String { - concat("witness-floor-", resolve_witness_floor_group_suffix(fx: fx)) -} - // SUPERSESSION IS DISABLED, AND THIS ROW IS THE WHOLE POLICY. // // This value decides whether GitHub cancels an in-flight run when a newer one joins the group. It diff --git a/dag/test/claim/gha_expression_roundtrip_test.dag b/dag/test/claim/gha_expression_roundtrip_test.dag index b947f400733..0d0b0f92ab4 100644 --- a/dag/test/claim/gha_expression_roundtrip_test.dag +++ b/dag/test/claim/gha_expression_roundtrip_test.dag @@ -233,3 +233,24 @@ test fn witness_not_equals_renders_ne_operator() -> Bool { serialize_expression(expression: not_equals_empty_input_expr()) == "github.event.inputs.expected_healed_sha != ''" } +fn unused_github_path(path: List) -> GithubPathResolution { + GithubPathUnmodeled +} + +test fn witness_not_equals_evaluates_false_when_equal() -> Bool { + match evaluate_expression( + expression: NotEquals { left: StringLiteral { value: "a" }, right: StringLiteral { value: "a" } }, + github_path: unused_github_path + ) { + ExpressionValue { value: v } => v == "" + ExpressionEvaluationRefused { cause: _ } => false + } + && match evaluate_expression( + expression: NotEquals { left: StringLiteral { value: "a" }, right: StringLiteral { value: "b" } }, + github_path: unused_github_path + ) { + ExpressionValue { value: v } => v == "true" + ExpressionEvaluationRefused { cause: _ } => false + } +} + diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index a38cd753cde..17b7fd3c622 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } import gunbc.fleet_converge_workflow { fleet_converge_workflow } @@ -12,6 +12,19 @@ import v2.std.diagnostic { Outcome } import extdeps.languages.yaml.gha_workflow { pr_activity_string } import extdeps.github.actions { PullRequestActivity, Opened, Synchronize, Reopened, ReadyForReview, Closed, github_event_name_pull_request, github_event_name_push, github_event_name_merge_group, github_event_name_workflow_dispatch } import extdeps.github.actions { RunStep, UsesStep } +import extdeps.github.expressions { + ContextAccess, + Github, + FunctionCall, + Format, + StringLiteral, + ExpressionEvaluationRefused, + ExpressionValue, + UnmodeledContext, + UnmodeledFormat, + FormatArityNotOneSubstitution, + github_path_key +} // THE CI PHASES ARE THE PROGRAM'S TO SEQUENCE, NOT THE JOB'S (operator directive, 2026-08-20: // "i would much prefer if it was all handled within the witnesses step and within the gunbc @@ -466,6 +479,13 @@ test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subjec } } +fn group_resolved_as(fx: WitnessFloorGroupFixture, expected: String) -> Bool { + match resolve_witness_floor_concurrency_group(fx: fx) { + WitnessFloorGroupResolved { group: g } => g == expected + WitnessFloorGroupRefused { cause: _ } => false + } +} + test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_cancel() -> Bool { let mg = WitnessFloorGroupFixture { event: github_event_name_merge_group, @@ -519,13 +539,13 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc github_ref: "refs/heads/main", dispatch_inputs: [] } - resolve_witness_floor_concurrency_group(fx: mg) == "witness-floor-mg-mgsha" - && resolve_witness_floor_concurrency_group(fx: heal) == "witness-floor-heal-healsha" - && resolve_witness_floor_concurrency_group(fx: heal_empty) == "witness-floor-run-rid" - && resolve_witness_floor_concurrency_group(fx: bare_dispatch) == "witness-floor-run-rid" - && resolve_witness_floor_concurrency_group(fx: pr) == "witness-floor-pr-7" - && resolve_witness_floor_concurrency_group(fx: push) == "witness-floor-refs/heads/main" - && resolve_witness_floor_concurrency_group(fx: push) != "witness-floor-pr-7" + group_resolved_as(fx: mg, expected: "witness-floor-mg-mgsha") + && group_resolved_as(fx: heal, expected: "witness-floor-heal-healsha") + && group_resolved_as(fx: heal_empty, expected: "witness-floor-run-rid") + && group_resolved_as(fx: bare_dispatch, expected: "witness-floor-run-rid") + && group_resolved_as(fx: pr, expected: "witness-floor-pr-7") + && group_resolved_as(fx: push, expected: "witness-floor-refs/heads/main") + && !group_resolved_as(fx: push, expected: "witness-floor-pr-7") && match expected_witness_floor_yml() { WitnessFloorGenerated { content: yml } => string_contains( @@ -565,3 +585,33 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc WitnessFloorGenerationRefused { reason: _ } => false } } + +test fn w_RED_unmodeled_format_arity_and_base_ref_refuse() -> Bool { + let fx = WitnessFloorGroupFixture { + event: github_event_name_push, + sha: "mgsha", + run_id: "rid", + pull_request_number: "7", + github_ref: "refs/heads/main", + dispatch_inputs: [] + } + let two_arg_format = FunctionCall { + function: Format, + args: [StringLiteral { value: "x-{0}" }, StringLiteral { value: "a" }, StringLiteral { value: "b" }] + } + let base_ref_expr = ContextAccess { context: Github, path: ["base_ref"] } + match evaluate_against_group_fixture(expression: two_arg_format, fx: fx) { + ExpressionEvaluationRefused { cause: UnmodeledFormat { cause: FormatArityNotOneSubstitution { argument_count: n } } } => n == 3 + ExpressionEvaluationRefused { cause: _ } => false + ExpressionValue { value: _ } => false + } + && match evaluate_against_group_fixture(expression: base_ref_expr, fx: fx) { + ExpressionEvaluationRefused { cause: UnmodeledContext { context: Github, path: p } } => github_path_key(path: p) == "base_ref" + ExpressionEvaluationRefused { cause: _ } => false + ExpressionValue { value: _ } => false + } + && match resolve_witness_floor_concurrency_group(fx: fx) { + WitnessFloorGroupResolved { group: g } => g == "witness-floor-refs/heads/main" + WitnessFloorGroupRefused { cause: _ } => false + } +} From d7525959619884b4c729d04236551d2ad01e6710 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 07:09:15 +0000 Subject: [PATCH 19/27] Derive mutable-head event membership and cancel OR from one roster. Classifier and cancel emission both folded the same {pull_request, push} set by hand, so they could disagree; the four-cell join now pins supersession_available_under on push as well. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 32 +++++++++++++++---- .../claim/run_disposition_witness_test.dag | 1 + 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 0a779f219e5..2f8853fd2ce 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -529,17 +529,35 @@ fn supersession_event_expression(event: String) -> String { concat(concat("${{ github.event_name == '", event), "' }}") } +// THE MUTABLE-HEAD EVENT SET IS ONE ROSTER, same as the queue axis below: classifier membership +// and the OR'd cancel Expression both fold this list, so they cannot disagree by a second edit. +data mutable_ci_head_event_names: List = [ + github_event_name_pull_request, + github_event_name_push, +] + fn is_mutable_ci_head_event(event: String) -> Bool { - event == github_event_name_pull_request || event == github_event_name_push + fold_list( + xs: mutable_ci_head_event_names, + empty: false, + cons: fn(acc, e) { acc || e == event } + ) +} + +fn mutable_head_runs_cancel_ast() -> Expression { + match list_head(xs: mutable_ci_head_event_names) { + HeadAbsent => github_event_name_equals(event: "") + HeadFound { value: first } => + fold_list( + xs: skip(xs: mutable_ci_head_event_names, n: 1), + empty: github_event_name_equals(event: first), + cons: fn(acc, e) { gha_or(left: acc, right: github_event_name_equals(event: e)) } + ) + } } fn mutable_head_runs_cancel_expression() -> String { - interpolate( - expression: gha_or( - left: github_event_name_equals(event: github_event_name_pull_request), - right: github_event_name_equals(event: github_event_name_push) - ) - ) + interpolate(expression: mutable_head_runs_cancel_ast()) } fn cancel_in_progress_of_policy(policy: RunningSupersessionPolicy) -> CancelInProgressSpec { diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 2d5d942cf34..b092e7f5f3e 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -651,6 +651,7 @@ test fn w_running_cancel_four_cell_through_the_producer() -> Bool { && supersession_available_under(policy: unproven, event: "pull_request") == false && supersession_available_under(policy: unproven, event: "merge_group") == false && supersession_available_under(policy: safe, event: "pull_request") == true + && supersession_available_under(policy: safe, event: "push") == true && supersession_available_under(policy: safe, event: "merge_group") == false && unproven_cancel && safe_cancel From 99d0f59e26bf5a38e27c86c38339e5fe9c0d2ff9 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 07:44:06 +0000 Subject: [PATCH 20/27] Treat only the empty string as falsy in the GHA expression evaluator. The operators page falsifies boolean false and number 0, not the strings "false" and "0"; Equals already encodes boolean false as "". Co-authored-by: Cursor --- dag/extdeps/github/expressions.dag | 12 +++++++----- .../claim/gha_expression_roundtrip_test.dag | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/dag/extdeps/github/expressions.dag b/dag/extdeps/github/expressions.dag index f6941db1116..a99cfe19ee4 100644 --- a/dag/extdeps/github/expressions.dag +++ b/dag/extdeps/github/expressions.dag @@ -554,12 +554,14 @@ fn any_template_accesses_runner_var_via_env(templates: List) acc || template_accesses_runner_var_via_env(template: template)) } -// GITHUB ACTIONS TRUTHINESS (github_expression_operators_authority, operators): false, 0, and -// empty string are falsy. This evaluator's string encoding of a Boolean false is the empty -// string; "false" and "0" are also falsy. format() substitution is github_expression_format_authority -// `format` with replaceValue0 as `{0}`. +// GITHUB ACTIONS TRUTHINESS (github_expression_operators_authority, operators): the falsy +// values are the boolean false, the numbers 0 and -0, empty string, and null. Non-empty strings +// are truthy, including the strings "false" and "0" — `${{ 'false' && 'x' }}` is `'x'`. This +// evaluator is string-only; Equals/NotEquals encode a boolean false as the empty string, so +// treating the string "false" as falsy would invent a string-typed boolean GitHub does not have. +// format() substitution is github_expression_format_authority `format` with replaceValue0 as `{0}`. fn gha_value_is_truthy(s: String) -> Bool { - s != "" && s != "false" && s != "0" + s != "" } fn github_path_key(path: List) -> String { diff --git a/dag/test/claim/gha_expression_roundtrip_test.dag b/dag/test/claim/gha_expression_roundtrip_test.dag index 0d0b0f92ab4..b1f63d738bc 100644 --- a/dag/test/claim/gha_expression_roundtrip_test.dag +++ b/dag/test/claim/gha_expression_roundtrip_test.dag @@ -254,3 +254,22 @@ test fn witness_not_equals_evaluates_false_when_equal() -> Bool { } } +fn and_literals(left: String, right: String) -> Expression { + LogicalAnd { left: StringLiteral { value: left }, right: StringLiteral { value: right } } +} + +test fn witness_string_false_and_zero_are_truthy() -> Bool { + match evaluate_expression(expression: and_literals(left: "false", right: "x"), github_path: unused_github_path) { + ExpressionValue { value: v } => v == "x" + ExpressionEvaluationRefused { cause: _ } => false + } + && match evaluate_expression(expression: and_literals(left: "0", right: "x"), github_path: unused_github_path) { + ExpressionValue { value: v } => v == "x" + ExpressionEvaluationRefused { cause: _ } => false + } + && match evaluate_expression(expression: and_literals(left: "", right: "x"), github_path: unused_github_path) { + ExpressionValue { value: v } => v == "" + ExpressionEvaluationRefused { cause: _ } => false + } +} + From 7ef183da26e3fabe6476de36b50b1ae5dfbda9a8 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 08:09:30 +0000 Subject: [PATCH 21/27] Keep running-cancel restoration to pull_request; do not cancel running main. Occupancy grouping still keys push by github.ref (pending replacement). A landed main SHA's running jobs are not lawful supersession without a declared 4b(3) drop. Co-authored-by: Cursor --- dag/gunbc/run_disposition.dag | 2 +- .../main_per_commit_floor_verdict.dag | 2 +- dag/gunbc/witness/witness_floor_workflow.dag | 11 ++++++---- .../claim/run_disposition_witness_test.dag | 22 +++++++++---------- docs/design-rung-drops.md | 2 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/dag/gunbc/run_disposition.dag b/dag/gunbc/run_disposition.dag index 691a26d43d0..319d17fcb7d 100644 --- a/dag/gunbc/run_disposition.dag +++ b/dag/gunbc/run_disposition.dag @@ -534,7 +534,7 @@ fn supersession_availability_rule_text_under(spec: RunningSupersessionPolicy) -> SupersedeRunningForEvent { event: e } => concat("a RUNNING run may be superseded only for the event ", concat(e, "; a running run cancelled under any other event is external, and a pending run may be replaced by the queue regardless")) SupersedeMutableHeadRuns => - "a RUNNING run may be superseded for pull_request and push (newest mutable head wins the whole workflow); merge_group, heal dispatch keyed by expected_healed_sha, and other dispatch are not cancelled across subjects; a pending run may be replaced by the queue regardless" + "a RUNNING run may be superseded for events in mutable_ci_head_event_names (currently pull_request); a running run cancelled under any other event is external, and a pending run may be replaced by the queue regardless" } } diff --git a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag index 53539e527ec..9e78129db2f 100644 --- a/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag +++ b/dag/gunbc/rung_drop/main_per_commit_floor_verdict.dag @@ -29,6 +29,6 @@ data main_per_commit_floor_verdict: RungDrop = RungDrop { population: [ "push events on refs/heads/main" ], - restoration_trigger: "every superseded pending refs/heads/main SHA receives a terminal adjudication, bound to that SHA, of every required acceptance obligation -- executed and passed, or coverage by a superseding execution proved. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row" + restoration_trigger: "every superseded pending refs/heads/main SHA receives a terminal adjudication, bound to that SHA, of every required acceptance obligation -- executed and passed, or coverage by a superseding execution proved. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. Fleet cancellation safety (running_supersession_for_fleet_standing restoring pull_request-only running cancel) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row" } } diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 2f8853fd2ce..39a66440114 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -512,7 +512,8 @@ fn running_supersession_for_fleet_standing( ) -> RunningSupersessionPolicy { match standing { FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => NeverSupersedeRunning - FleetCancellationSafe { hosts: _ } => SupersedeMutableHeadRuns + FleetCancellationSafe { hosts: _ } => + SupersedeRunningForEvent { event: github_event_name_pull_request } } } @@ -529,11 +530,13 @@ fn supersession_event_expression(event: String) -> String { concat(concat("${{ github.event_name == '", event), "' }}") } -// THE MUTABLE-HEAD EVENT SET IS ONE ROSTER, same as the queue axis below: classifier membership -// and the OR'd cancel Expression both fold this list, so they cannot disagree by a second edit. +// RUNNING-CANCEL EVENTS ARE NOT THE GROUPING SET (review 63654). Occupancy grouping of push +// lives in witness_floor_group_branches (pending replacement on github.ref). This roster is the +// running-cancel event set for SupersedeMutableHeadRuns: classifier membership and the OR'd +// cancel Expression both fold it. Push is not a member — an older refs/heads/main SHA is landed +// work, and cancelling its RUNNING jobs is a rung drop that this PR does not declare. data mutable_ci_head_event_names: List = [ github_event_name_pull_request, - github_event_name_push, ] fn is_mutable_ci_head_event(event: String) -> Bool { diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index b092e7f5f3e..d66f2ba6254 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -41,7 +41,7 @@ import gunbc.witness_floor_workflow { NeverSupersedeRunning, SupersedeRunningForEvent, SupersedeMutableHeadRuns, - mutable_head_runs_cancel_expression, + supersession_event_expression, witness_floor_concurrency_policy, witness_floor_running_supersession_policy, concurrency_of_policy @@ -633,25 +633,25 @@ test fn w_running_cancel_four_cell_through_the_producer() -> Bool { SupersedeRunningForEvent { event: _ } => false SupersedeMutableHeadRuns => false } - let safe_is_mutable = match safe { - SupersedeMutableHeadRuns => true + let safe_is_pr = match safe { + SupersedeRunningForEvent { event: e } => e == "pull_request" NeverSupersedeRunning => false - SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } let unproven_cancel = match cancel_in_progress_of_policy(policy: unproven) { CancelInProgressBool { value: b } => b == false CancelInProgressExpression { expression: _ } => false } let safe_cancel = match cancel_in_progress_of_policy(policy: safe) { - CancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() + CancelInProgressExpression { expression: e } => e == supersession_event_expression(event: "pull_request") CancelInProgressBool { value: _ } => false } unproven_is_never - && safe_is_mutable + && safe_is_pr && supersession_available_under(policy: unproven, event: "pull_request") == false && supersession_available_under(policy: unproven, event: "merge_group") == false && supersession_available_under(policy: safe, event: "pull_request") == true - && supersession_available_under(policy: safe, event: "push") == true + && supersession_available_under(policy: safe, event: "push") == false && supersession_available_under(policy: safe, event: "merge_group") == false && unproven_cancel && safe_cancel @@ -744,9 +744,9 @@ test fn w_a_fully_observed_safe_fleet_restores_supersession() -> Bool { safe_host(name: "srv2"), ]), ) { - SupersedeMutableHeadRuns => true + SupersedeRunningForEvent { event: e } => e == "pull_request" NeverSupersedeRunning => false - SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } @@ -820,9 +820,9 @@ test fn w_a_server_reclaiming_fleet_restores_supersession() -> Bool { match running_supersession_for_fleet_standing( standing: finalize_fleet_standing(acc: reclaiming_fleet_acc(), eligible_hosts: two_host_fleet()), ) { - SupersedeMutableHeadRuns => true + SupersedeRunningForEvent { event: e } => e == "pull_request" NeverSupersedeRunning => false - SupersedeRunningForEvent { event: _ } => false + SupersedeMutableHeadRuns => false } } diff --git a/docs/design-rung-drops.md b/docs/design-rung-drops.md index b4205d13f16..4040ec8b767 100644 --- a/docs/design-rung-drops.md +++ b/docs/design-rung-drops.md @@ -212,4 +212,4 @@ The v2 native route (emitted-native compiler over the derived v2.test.* universe ### every landed main SHA obtains its own terminal verdict for every required acceptance obligation on the workflow — declared 2026-09-11 -every landed main SHA obtains its own terminal verdict for every required acceptance obligation on the workflow: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: every superseded pending refs/heads/main SHA receives a terminal adjudication, bound to that SHA, of every required acceptance obligation -- executed and passed, or coverage by a superseding execution proved. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. Fleet cancellation safety (running_supersession_for_fleet_standing / SupersedeMutableHeadRuns) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row. +every landed main SHA obtains its own terminal verdict for every required acceptance obligation on the workflow: RUNG DROP, mechanically preventable -> mitigatable (replacement staged: witness_floor_concurrency_group keys push events by github.ref under the 2026-09-10 occupancy ruling; cancel-in-progress stays false). Population: push events on refs/heads/main. Restored when: every superseded pending refs/heads/main SHA receives a terminal adjudication, bound to that SHA, of every required acceptance obligation -- executed and passed, or coverage by a superseding execution proved. SUFFICIENT FOR: per-commit attribution and bisection grain on main, so a SHA GitHub removed while pending is not silent. Fleet cancellation safety (running_supersession_for_fleet_standing restoring pull_request-only running cancel) lives on the running-cancellation axis and does not retire this evidence-grain drop. Re-adding unique run_id to the group string is an artifact and does not discharge the row. From b13a3b9faf6165379335568327615304a6fc8d23 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 08:37:01 +0000 Subject: [PATCH 22/27] Wire the safe fleet arm to SupersedeMutableHeadRuns over the PR-only roster. The producer was restoring a pull_request literal beside an unused roster and cancel AST, so running-cancel membership had two homes. Classifier, cancel emission, and the four-cell join all read that one list; push stays off it. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 3 +-- .../claim/run_disposition_witness_test.dag | 20 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 39a66440114..63b6d4ac2ad 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -512,8 +512,7 @@ fn running_supersession_for_fleet_standing( ) -> RunningSupersessionPolicy { match standing { FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => NeverSupersedeRunning - FleetCancellationSafe { hosts: _ } => - SupersedeRunningForEvent { event: github_event_name_pull_request } + FleetCancellationSafe { hosts: _ } => SupersedeMutableHeadRuns } } diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index d66f2ba6254..61c6ae8bda2 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -41,7 +41,7 @@ import gunbc.witness_floor_workflow { NeverSupersedeRunning, SupersedeRunningForEvent, SupersedeMutableHeadRuns, - supersession_event_expression, + mutable_head_runs_cancel_expression, witness_floor_concurrency_policy, witness_floor_running_supersession_policy, concurrency_of_policy @@ -633,21 +633,21 @@ test fn w_running_cancel_four_cell_through_the_producer() -> Bool { SupersedeRunningForEvent { event: _ } => false SupersedeMutableHeadRuns => false } - let safe_is_pr = match safe { - SupersedeRunningForEvent { event: e } => e == "pull_request" + let safe_is_mutable = match safe { + SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeMutableHeadRuns => false + SupersedeRunningForEvent { event: _ } => false } let unproven_cancel = match cancel_in_progress_of_policy(policy: unproven) { CancelInProgressBool { value: b } => b == false CancelInProgressExpression { expression: _ } => false } let safe_cancel = match cancel_in_progress_of_policy(policy: safe) { - CancelInProgressExpression { expression: e } => e == supersession_event_expression(event: "pull_request") + CancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() CancelInProgressBool { value: _ } => false } unproven_is_never - && safe_is_pr + && safe_is_mutable && supersession_available_under(policy: unproven, event: "pull_request") == false && supersession_available_under(policy: unproven, event: "merge_group") == false && supersession_available_under(policy: safe, event: "pull_request") == true @@ -744,9 +744,9 @@ test fn w_a_fully_observed_safe_fleet_restores_supersession() -> Bool { safe_host(name: "srv2"), ]), ) { - SupersedeRunningForEvent { event: e } => e == "pull_request" + SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeMutableHeadRuns => false + SupersedeRunningForEvent { event: _ } => false } } @@ -820,9 +820,9 @@ test fn w_a_server_reclaiming_fleet_restores_supersession() -> Bool { match running_supersession_for_fleet_standing( standing: finalize_fleet_standing(acc: reclaiming_fleet_acc(), eligible_hosts: two_host_fleet()), ) { - SupersedeRunningForEvent { event: e } => e == "pull_request" + SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeMutableHeadRuns => false + SupersedeRunningForEvent { event: _ } => false } } From 7133546e24fae31794695ba10b9ad40f910a606d Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 08:39:39 +0000 Subject: [PATCH 23/27] Delete the unused SupersedeMutableHeadRuns surface. Safe-fleet restore is SupersedeRunningForEvent { pull_request }; a one-element mutable-head roster was a nickname of that constructor. Four-cell still pins that push running-cancel stays false. witnesses.yml is unchanged. Co-authored-by: Cursor --- dag/gunbc/run_disposition.dag | 5 --- dag/gunbc/witness/witness_floor_workflow.dag | 41 +------------------ .../claim/run_disposition_witness_test.dag | 27 ++++-------- ...or_workflow_consolidation_witness_test.dag | 4 +- 4 files changed, 11 insertions(+), 66 deletions(-) diff --git a/dag/gunbc/run_disposition.dag b/dag/gunbc/run_disposition.dag index 319d17fcb7d..5dca5ee5c20 100644 --- a/dag/gunbc/run_disposition.dag +++ b/dag/gunbc/run_disposition.dag @@ -28,8 +28,6 @@ import gunbc.witness_floor_workflow { RunningSupersessionPolicy, NeverSupersedeRunning, SupersedeRunningForEvent, - SupersedeMutableHeadRuns, - is_mutable_ci_head_event, witness_floor_running_supersession_policy } @@ -98,7 +96,6 @@ fn supersession_available_under(policy: RunningSupersessionPolicy, event: String match policy { NeverSupersedeRunning => false SupersedeRunningForEvent { event: e } => e == event - SupersedeMutableHeadRuns => is_mutable_ci_head_event(event: event) } } @@ -533,8 +530,6 @@ fn supersession_availability_rule_text_under(spec: RunningSupersessionPolicy) -> "no RUNNING run may be superseded: witnesses.yml cancel-in-progress is unconditionally false, so a cancellation after execution started is an eviction; a run cancelled while still PENDING may still be lawfully replaced by the queue" SupersedeRunningForEvent { event: e } => concat("a RUNNING run may be superseded only for the event ", concat(e, "; a running run cancelled under any other event is external, and a pending run may be replaced by the queue regardless")) - SupersedeMutableHeadRuns => - "a RUNNING run may be superseded for events in mutable_ci_head_event_names (currently pull_request); a running run cancelled under any other event is external, and a pending run may be replaced by the queue regardless" } } diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 63b6d4ac2ad..28cc64684f5 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -474,7 +474,6 @@ fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> Witn type RunningSupersessionPolicy = NeverSupersedeRunning | SupersedeRunningForEvent { event: String } - | SupersedeMutableHeadRuns // THE QUEUE AXIS IS THE SECOND HALF OF THE SAME FACT, and omitting it left the classifier carrying // a hardcoded copy of today's regime. ConcurrencySpec already distinguishes QueueMax from @@ -512,7 +511,8 @@ fn running_supersession_for_fleet_standing( ) -> RunningSupersessionPolicy { match standing { FleetCancellationNotSafe { unsafe_hosts: _, unobserved_hosts: _ } => NeverSupersedeRunning - FleetCancellationSafe { hosts: _ } => SupersedeMutableHeadRuns + FleetCancellationSafe { hosts: _ } => + SupersedeRunningForEvent { event: github_event_name_pull_request } } } @@ -529,46 +529,11 @@ fn supersession_event_expression(event: String) -> String { concat(concat("${{ github.event_name == '", event), "' }}") } -// RUNNING-CANCEL EVENTS ARE NOT THE GROUPING SET (review 63654). Occupancy grouping of push -// lives in witness_floor_group_branches (pending replacement on github.ref). This roster is the -// running-cancel event set for SupersedeMutableHeadRuns: classifier membership and the OR'd -// cancel Expression both fold it. Push is not a member — an older refs/heads/main SHA is landed -// work, and cancelling its RUNNING jobs is a rung drop that this PR does not declare. -data mutable_ci_head_event_names: List = [ - github_event_name_pull_request, -] - -fn is_mutable_ci_head_event(event: String) -> Bool { - fold_list( - xs: mutable_ci_head_event_names, - empty: false, - cons: fn(acc, e) { acc || e == event } - ) -} - -fn mutable_head_runs_cancel_ast() -> Expression { - match list_head(xs: mutable_ci_head_event_names) { - HeadAbsent => github_event_name_equals(event: "") - HeadFound { value: first } => - fold_list( - xs: skip(xs: mutable_ci_head_event_names, n: 1), - empty: github_event_name_equals(event: first), - cons: fn(acc, e) { gha_or(left: acc, right: github_event_name_equals(event: e)) } - ) - } -} - -fn mutable_head_runs_cancel_expression() -> String { - interpolate(expression: mutable_head_runs_cancel_ast()) -} - fn cancel_in_progress_of_policy(policy: RunningSupersessionPolicy) -> CancelInProgressSpec { match policy { NeverSupersedeRunning => CancelInProgressBool { value: false } SupersedeRunningForEvent { event: e } => CancelInProgressExpression { expression: supersession_event_expression(event: e) } - SupersedeMutableHeadRuns => - CancelInProgressExpression { expression: mutable_head_runs_cancel_expression() } } } @@ -589,8 +554,6 @@ fn queue_max_cancel_of_policy(policy: RunningSupersessionPolicy) -> CancelInProg NeverSupersedeRunning => QueueMaxCancelInProgressFalse SupersedeRunningForEvent { event: e } => QueueMaxCancelInProgressExpression { expression: supersession_event_expression(event: e) } - SupersedeMutableHeadRuns => - QueueMaxCancelInProgressExpression { expression: mutable_head_runs_cancel_expression() } } } diff --git a/dag/test/claim/run_disposition_witness_test.dag b/dag/test/claim/run_disposition_witness_test.dag index 61c6ae8bda2..506806e4560 100644 --- a/dag/test/claim/run_disposition_witness_test.dag +++ b/dag/test/claim/run_disposition_witness_test.dag @@ -40,8 +40,7 @@ import gunbc.witness_floor_workflow { RunningSupersessionPolicy, NeverSupersedeRunning, SupersedeRunningForEvent, - SupersedeMutableHeadRuns, - mutable_head_runs_cancel_expression, + supersession_event_expression, witness_floor_concurrency_policy, witness_floor_running_supersession_policy, concurrency_of_policy @@ -631,23 +630,21 @@ test fn w_running_cancel_four_cell_through_the_producer() -> Bool { let unproven_is_never = match unproven { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } - let safe_is_mutable = match safe { - SupersedeMutableHeadRuns => true + let safe_is_pr = match safe { + SupersedeRunningForEvent { event: e } => e == "pull_request" NeverSupersedeRunning => false - SupersedeRunningForEvent { event: _ } => false } let unproven_cancel = match cancel_in_progress_of_policy(policy: unproven) { CancelInProgressBool { value: b } => b == false CancelInProgressExpression { expression: _ } => false } let safe_cancel = match cancel_in_progress_of_policy(policy: safe) { - CancelInProgressExpression { expression: e } => e == mutable_head_runs_cancel_expression() + CancelInProgressExpression { expression: e } => e == supersession_event_expression(event: "pull_request") CancelInProgressBool { value: _ } => false } unproven_is_never - && safe_is_mutable + && safe_is_pr && supersession_available_under(policy: unproven, event: "pull_request") == false && supersession_available_under(policy: unproven, event: "merge_group") == false && supersession_available_under(policy: safe, event: "pull_request") == true @@ -676,7 +673,6 @@ test fn w_an_unobserved_fleet_refuses_to_supersede_a_running_run() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -694,7 +690,6 @@ test fn w_a_single_unobserved_host_holds_the_containment() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -710,7 +705,6 @@ test fn w_a_single_unreadable_host_holds_the_containment() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -730,7 +724,6 @@ test fn w_a_host_still_running_the_cooperative_fifo_is_derived_unsafe() -> Bool ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -744,9 +737,8 @@ test fn w_a_fully_observed_safe_fleet_restores_supersession() -> Bool { safe_host(name: "srv2"), ]), ) { - SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeRunningForEvent { event: _ } => false + SupersedeRunningForEvent { event: e } => e == "pull_request" } } @@ -756,7 +748,6 @@ test fn w_the_live_fleet_standing_still_refuses_running_supersession() -> Bool { match running_supersession_for_fleet_standing(standing: fleet_compile_cancellation_standing) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -773,7 +764,6 @@ test fn w_a_partially_observed_fleet_is_not_safe() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -788,7 +778,6 @@ test fn w_an_observation_outside_the_roster_does_not_complete_it() -> Bool { ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } @@ -820,9 +809,8 @@ test fn w_a_server_reclaiming_fleet_restores_supersession() -> Bool { match running_supersession_for_fleet_standing( standing: finalize_fleet_standing(acc: reclaiming_fleet_acc(), eligible_hosts: two_host_fleet()), ) { - SupersedeMutableHeadRuns => true NeverSupersedeRunning => false - SupersedeRunningForEvent { event: _ } => false + SupersedeRunningForEvent { event: e } => e == "pull_request" } } @@ -845,6 +833,5 @@ test fn w_one_losing_host_among_reclaiming_hosts_holds_the_containment() -> Bool ) { NeverSupersedeRunning => true SupersedeRunningForEvent { event: _ } => false - SupersedeMutableHeadRuns => false } } diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 17b7fd3c622..036285fa722 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, mutable_head_runs_cancel_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, supersession_event_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } import gunbc.fleet_converge_workflow { fleet_converge_workflow } @@ -581,7 +581,7 @@ test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_canc pattern: concat("github.event_name == '", concat(github_event_name_push, "' && format('pr-{0}'")) ) && string_contains(s: yml, pattern: "cancel-in-progress: false") - && !string_contains(s: yml, pattern: mutable_head_runs_cancel_expression()) + && !string_contains(s: yml, pattern: supersession_event_expression(event: github_event_name_pull_request)) WitnessFloorGenerationRefused { reason: _ } => false } } From 8f9c28f3434b66595237190cf89c96d688c84620 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 09:01:53 +0000 Subject: [PATCH 24/27] Name grouping and the main-verdict drop instead of citing the group above. The running-cancel annotation claimed main already accepted N concurrent runs; pending main replacement is the occupancy grouping trade, declared as main_per_commit_floor_verdict, and running cancel stays false. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index 28cc64684f5..a0e8a75ec29 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -447,9 +447,11 @@ fn resolve_witness_floor_concurrency_group(fx: WitnessFloorGroupFixture) -> Witn // reseeded, still medians 22.8 minutes against srv4's 11.8. // // THE COST OF THIS ROW IS N CONCURRENT RUNS PER PULL REQUEST where one used to survive. That is -// the same trade the concurrency group above already accepts for main, for the same reason: stale -// work completing is expensive, and capacity silently disappearing is not recoverable. Cancelling -// is only cheaper than not cancelling while the permits come back. +// not the grouping trade: witness_floor_group_branches keys push by github.ref +// (RefAfterEvent), so GitHub may replace PENDING members of witness-floor-refs/heads/main — +// declared as gunbc.rung_drop.main_per_commit_floor_verdict — while this running-cancel row stays +// false for every event, including those main runs. Cancelling a RUNNING job is only cheaper than +// letting it finish while the permits come back; replacing a pending one never held a permit. // // DISSOLVE-ON, and it is the CAPABILITY rather than any artifact: no client of the required floor // holds a host-persistent cooperative permit -- either the ctrl host FIFO is deleted and compile From db677c93ec3ebce1cacf19a1da71e086728cb377 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 10:16:56 +0000 Subject: [PATCH 25/27] Split grouping and PR-activity witnesses off the full YAML serialize. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each event fixture is its own claim; group-expression text is interpolated from the AST; the activity set is the roster the trigger already emits. The combined YAML walk was 502–600 ms against a 500 ms deadline. Co-authored-by: Cursor --- dag/gunbc/witness/witness_floor_workflow.dag | 8 +- ...or_workflow_consolidation_witness_test.dag | 204 +++++++++--------- 2 files changed, 109 insertions(+), 103 deletions(-) diff --git a/dag/gunbc/witness/witness_floor_workflow.dag b/dag/gunbc/witness/witness_floor_workflow.dag index a0e8a75ec29..ca1d075ad86 100644 --- a/dag/gunbc/witness/witness_floor_workflow.dag +++ b/dag/gunbc/witness/witness_floor_workflow.dag @@ -2686,6 +2686,12 @@ fn required_lanes_aggregate_job() -> Job { // from exactly these, so a lane added here is a lane in the emitted yaml, and a reader folding // `witness_floor_lane_jobs` covers every witness-bearing lane by construction rather than by a // hand-listed roster that could fall behind. +data witness_floor_pull_request_activity_types: List = [ + Opened, + Synchronize, + Reopened, +] + fn witness_floor_triggers() -> List { [ WorkflowDispatch { inputs: [ci_heal_expected_healed_sha_input] }, @@ -2693,7 +2699,7 @@ fn witness_floor_triggers() -> List { Push { branches: [gunbc_default_branch_name], paths: [] }, PullRequest { branches: [gunbc_default_branch_name], - types: [Opened, Synchronize, Reopened] + types: witness_floor_pull_request_activity_types } ] } diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 036285fa722..38b30feceb0 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,7 +3,7 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, supersession_event_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, supersession_event_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture, witness_floor_pull_request_activity_types, witness_floor_group_expression } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } import gunbc.fleet_converge_workflow { fleet_converge_workflow } @@ -13,6 +13,7 @@ import extdeps.languages.yaml.gha_workflow { pr_activity_string } import extdeps.github.actions { PullRequestActivity, Opened, Synchronize, Reopened, ReadyForReview, Closed, github_event_name_pull_request, github_event_name_push, github_event_name_merge_group, github_event_name_workflow_dispatch } import extdeps.github.actions { RunStep, UsesStep } import extdeps.github.expressions { + interpolate, ContextAccess, Github, FunctionCall, @@ -467,16 +468,25 @@ test fn the_unrenderable_gate_refusal_serializes_and_stops_the_line() -> Bool { // over-deletion an absence-only row would have passed). Both went FAIL; the unmutated tree went // PASS. test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subject() -> Bool { - match expected_witness_floor_yml() { - WitnessFloorGenerated { content: yml } => - string_contains( - s: yml, - pattern: concat(" types: [", concat(pr_activity_string(a: Opened), concat(", ", concat(pr_activity_string(a: Synchronize), concat(", ", concat(pr_activity_string(a: Reopened), "]")))))) - ) - && !string_contains(s: yml, pattern: pr_activity_string(a: ReadyForReview)) - && !string_contains(s: yml, pattern: pr_activity_string(a: Closed)) - WitnessFloorGenerationRefused { reason: _ } => false - } + let types = witness_floor_pull_request_activity_types + length(xs: types) == 3 + && count_where(xs: types, predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Opened) }) == 1 + && count_where(xs: types, predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Synchronize) }) == 1 + && count_where(xs: types, predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Reopened) }) == 1 +} + +test fn w_RED_the_pull_request_activity_set_excludes_ready_for_review() -> Bool { + count_where( + xs: witness_floor_pull_request_activity_types, + predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: ReadyForReview) } + ) == 0 +} + +test fn w_RED_the_pull_request_activity_set_excludes_closed() -> Bool { + count_where( + xs: witness_floor_pull_request_activity_types, + predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Closed) } + ) == 0 } fn group_resolved_as(fx: WitnessFloorGroupFixture, expected: String) -> Bool { @@ -486,104 +496,94 @@ fn group_resolved_as(fx: WitnessFloorGroupFixture, expected: String) -> Bool { } } -test fn w_concurrency_group_resolves_each_event_fixture_and_refuses_running_cancel() -> Bool { - let mg = WitnessFloorGroupFixture { - event: github_event_name_merge_group, +fn group_fx(event: String, inputs: List) -> WitnessFloorGroupFixture { + WitnessFloorGroupFixture { + event: event, sha: "mgsha", run_id: "rid", pull_request_number: "7", github_ref: "refs/heads/main", - dispatch_inputs: [] + dispatch_inputs: inputs } - let heal = WitnessFloorGroupFixture { - event: github_event_name_workflow_dispatch, - sha: "mgsha", - run_id: "rid", - pull_request_number: "7", - github_ref: "refs/heads/main", - dispatch_inputs: [ - DispatchInputBinding { name: ci_heal_expected_healed_sha_input_name, value: "healsha" } - ] - } - let bare_dispatch = WitnessFloorGroupFixture { - event: github_event_name_workflow_dispatch, - sha: "mgsha", - run_id: "rid", - pull_request_number: "7", - github_ref: "refs/heads/main", - dispatch_inputs: [] - } - let heal_empty = WitnessFloorGroupFixture { - event: github_event_name_workflow_dispatch, - sha: "mgsha", - run_id: "rid", - pull_request_number: "7", - github_ref: "refs/heads/main", - dispatch_inputs: [ - DispatchInputBinding { name: ci_heal_expected_healed_sha_input_name, value: "" } - ] - } - let pr = WitnessFloorGroupFixture { - event: github_event_name_pull_request, - sha: "mgsha", - run_id: "rid", - pull_request_number: "7", - github_ref: "refs/heads/main", - dispatch_inputs: [] - } - let push = WitnessFloorGroupFixture { - event: github_event_name_push, - sha: "mgsha", - run_id: "rid", - pull_request_number: "7", - github_ref: "refs/heads/main", - dispatch_inputs: [] - } - group_resolved_as(fx: mg, expected: "witness-floor-mg-mgsha") - && group_resolved_as(fx: heal, expected: "witness-floor-heal-healsha") - && group_resolved_as(fx: heal_empty, expected: "witness-floor-run-rid") - && group_resolved_as(fx: bare_dispatch, expected: "witness-floor-run-rid") - && group_resolved_as(fx: pr, expected: "witness-floor-pr-7") - && group_resolved_as(fx: push, expected: "witness-floor-refs/heads/main") - && !group_resolved_as(fx: push, expected: "witness-floor-pr-7") - && match expected_witness_floor_yml() { - WitnessFloorGenerated { content: yml } => - string_contains( - s: yml, - pattern: concat("github.event_name == '", concat(github_event_name_merge_group, "' && format('mg-{0}', github.sha)")) - ) - && string_contains( - s: yml, - pattern: concat( - "github.event_name == '", - concat( - github_event_name_workflow_dispatch, - concat( - "' && github.event.inputs.", - concat( - ci_heal_expected_healed_sha_input_name, - concat(" != '' && format('heal-{0}', github.event.inputs.", concat(ci_heal_expected_healed_sha_input_name, ")")) - ) - ) - ) +} + +test fn w_concurrency_group_resolves_merge_group_to_mg_sha() -> Bool { + group_resolved_as( + fx: group_fx(event: github_event_name_merge_group, inputs: []), + expected: "witness-floor-mg-mgsha" + ) +} + +test fn w_concurrency_group_resolves_heal_dispatch_to_heal_sha() -> Bool { + group_resolved_as( + fx: group_fx( + event: github_event_name_workflow_dispatch, + inputs: [DispatchInputBinding { name: ci_heal_expected_healed_sha_input_name, value: "healsha" }] + ), + expected: "witness-floor-heal-healsha" + ) +} + +test fn w_concurrency_group_resolves_empty_heal_input_to_run_id() -> Bool { + group_resolved_as( + fx: group_fx( + event: github_event_name_workflow_dispatch, + inputs: [DispatchInputBinding { name: ci_heal_expected_healed_sha_input_name, value: "" }] + ), + expected: "witness-floor-run-rid" + ) +} + +test fn w_concurrency_group_resolves_bare_dispatch_to_run_id() -> Bool { + group_resolved_as( + fx: group_fx(event: github_event_name_workflow_dispatch, inputs: []), + expected: "witness-floor-run-rid" + ) +} + +test fn w_concurrency_group_resolves_pull_request_to_pr_number() -> Bool { + group_resolved_as( + fx: group_fx(event: github_event_name_pull_request, inputs: []), + expected: "witness-floor-pr-7" + ) +} + +test fn w_concurrency_group_resolves_push_to_github_ref() -> Bool { + group_resolved_as( + fx: group_fx(event: github_event_name_push, inputs: []), + expected: "witness-floor-refs/heads/main" + ) +} + +test fn w_concurrency_group_push_is_not_grouped_as_a_pr() -> Bool { + !group_resolved_as( + fx: group_fx(event: github_event_name_push, inputs: []), + expected: "witness-floor-pr-7" + ) +} + +test fn w_emitted_group_expression_names_each_event_branch() -> Bool { + let s = interpolate(expression: witness_floor_group_expression()) + string_contains(s: s, pattern: concat("github.event_name == '", concat(github_event_name_merge_group, "' && format('mg-{0}', github.sha)"))) + && string_contains( + s: s, + pattern: concat( + "github.event_name == '", + concat( + github_event_name_workflow_dispatch, + concat( + "' && github.event.inputs.", + concat( + ci_heal_expected_healed_sha_input_name, + concat(" != '' && format('heal-{0}', github.event.inputs.", concat(ci_heal_expected_healed_sha_input_name, ")")) ) ) - && string_contains( - s: yml, - pattern: concat("github.event_name == '", concat(github_event_name_pull_request, "' && format('pr-{0}', github.event.pull_request.number)")) - ) - && string_contains( - s: yml, - pattern: concat("github.event_name == '", concat(github_event_name_push, "' && github.ref")) - ) - && !string_contains( - s: yml, - pattern: concat("github.event_name == '", concat(github_event_name_push, "' && format('pr-{0}'")) - ) - && string_contains(s: yml, pattern: "cancel-in-progress: false") - && !string_contains(s: yml, pattern: supersession_event_expression(event: github_event_name_pull_request)) - WitnessFloorGenerationRefused { reason: _ } => false - } + ) + ) + ) + && string_contains(s: s, pattern: concat("github.event_name == '", concat(github_event_name_pull_request, "' && format('pr-{0}', github.event.pull_request.number)"))) + && string_contains(s: s, pattern: concat("github.event_name == '", concat(github_event_name_push, "' && github.ref"))) + && !string_contains(s: s, pattern: concat("github.event_name == '", concat(github_event_name_push, "' && format('pr-{0}'"))) } test fn w_RED_unmodeled_format_arity_and_base_ref_refuse() -> Bool { From f052982c0f64676cab93355fc1cda8458dac2657 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 11:12:58 +0000 Subject: [PATCH 26/27] Restore trigger-types emission coverage without a full workflow serialize. The activity-set witness now serializes witness_floor_triggers through the same YAML projector the workflow emit uses, so a list change in the trigger still fails while the claim stays at 6ms CPU. Co-authored-by: Cursor --- ...loor_workflow_consolidation_witness_test.dag | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 38b30feceb0..40b029f590a 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -3,13 +3,14 @@ module test.claim.witness_floor_workflow_consolidation_witness_test import std.types { Bool, List, String } import v2.std.algebra { count_where, length, non_empty_to_list } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } -import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, supersession_event_expression, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture, witness_floor_pull_request_activity_types, witness_floor_group_expression } +import gunbc.witness_floor_workflow { expected_witness_floor_yml, WitnessFloorGenerated, WitnessFloorGenerationRefused, build_lane_job_id, floor_lane_job_id, witness_floor_workflow_job_id, required_ci_measurement_publish_condition, required_ci_measurement_bound_steps, witness_floor_run_step, required_lanes_roster, witness_floor_lane_job_ids, build_lane_needs, witness_floor_lane_needs, ci_heal_expected_healed_sha_input_name, WitnessFloorGroupFixture, DispatchInputBinding, resolve_witness_floor_concurrency_group, WitnessFloorGroupResolved, WitnessFloorGroupRefused, evaluate_against_group_fixture, witness_floor_pull_request_activity_types, witness_floor_group_expression, witness_floor_triggers } import gunbc.fabric_witness_run { required_ci_lane_build, required_ci_lane_witnesses } import gunbc.required_lanes_gate { required_lanes_gate_unrenderable_stmts } import gunbc.fleet_converge_workflow { fleet_converge_workflow } import v2.workflow.bash_command_fold_serialize { bash_fold_serialize_program } import v2.std.diagnostic { Outcome } -import extdeps.languages.yaml.gha_workflow { pr_activity_string } +import extdeps.languages.yaml.gha_workflow { pr_activity_string, workflow_triggers_yaml } +import extdeps.languages.yaml.emit { serialize_yaml } import extdeps.github.actions { PullRequestActivity, Opened, Synchronize, Reopened, ReadyForReview, Closed, github_event_name_pull_request, github_event_name_push, github_event_name_merge_group, github_event_name_workflow_dispatch } import extdeps.github.actions { RunStep, UsesStep } import extdeps.github.expressions { @@ -468,11 +469,13 @@ test fn the_unrenderable_gate_refusal_serializes_and_stops_the_line() -> Bool { // over-deletion an absence-only row would have passed). Both went FAIL; the unmutated tree went // PASS. test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subject() -> Bool { - let types = witness_floor_pull_request_activity_types - length(xs: types) == 3 - && count_where(xs: types, predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Opened) }) == 1 - && count_where(xs: types, predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Synchronize) }) == 1 - && count_where(xs: types, predicate: fn(a) { pr_activity_string(a: a) == pr_activity_string(a: Reopened) }) == 1 + let yml = serialize_yaml(v: workflow_triggers_yaml(triggers: witness_floor_triggers())) + string_contains( + s: yml, + pattern: concat("types: [", concat(pr_activity_string(a: Opened), concat(", ", concat(pr_activity_string(a: Synchronize), concat(", ", concat(pr_activity_string(a: Reopened), "]")))))) + ) + && !string_contains(s: yml, pattern: pr_activity_string(a: ReadyForReview)) + && !string_contains(s: yml, pattern: pr_activity_string(a: Closed)) } test fn w_RED_the_pull_request_activity_set_excludes_ready_for_review() -> Bool { From 608c9ad5ec94ebc16b555c75bdb2041386fefe9a Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Fri, 11 Sep 2026 11:15:21 +0000 Subject: [PATCH 27/27] Record that trigger-types coverage is the on: fragment plus the YAML drift gate. The claim already serializes witness_floor_triggers through the workflow on: projector (6ms); the annotation names that composition so the full-file serialize is not reintroduced as the emission path. Co-authored-by: Cursor --- .../witness_floor_workflow_consolidation_witness_test.dag | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag index 40b029f590a..f4404a353a5 100644 --- a/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag +++ b/dag/test/claim/witness_floor_workflow_consolidation_witness_test.dag @@ -468,6 +468,14 @@ test fn the_unrenderable_gate_refusal_serializes_and_stops_the_line() -> Bool { // (the reversion this row exists to catch) and emptying the activity list entirely (the // over-deletion an absence-only row would have passed). Both went FAIL; the unmutated tree went // PASS. +// +// SOURCE->EMISSION IS THIS FRAGMENT, NOT A FULL WORKFLOW SERIALIZE. The bytes asserted here are +// `workflow_triggers_yaml(witness_floor_triggers())` -- the same projector `project_workflow_to_yaml` +// uses for the workflow `on:` block -- so a mutation that drops `witness_floor_pull_request_activity_types` +// from that trigger and inlines another list still fails. The heal/regen drift gate already +// establishes that committed witnesses.yml equals the full projection; fragment plus that gate +// cover the path end to end. A per-claim `expected_witness_floor_yml()` read is the whole-file +// serialize this row must not pay (measured 1823ms against the 500ms line). test fn w_RED_the_pull_request_activity_set_is_the_three_that_carry_a_new_subject() -> Bool { let yml = serialize_yaml(v: workflow_triggers_yaml(triggers: witness_floor_triggers())) string_contains(