From bceaa5b17a4f2b97ca548c654d3659c45d029019 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 10:13:29 +0000 Subject: [PATCH 1/3] Accepting wildcards cut 1: name the verdict classifier and the two route gates Three closed-coproduct matches whose wildcard arm returned the ACCEPTING answer, so an unhandled or future constructor silently inherited acceptance. Each now names every arm; no fallback was swapped for another. Three non_fold_residue rows deleted by hand with the receipt. - merge_group_run_carries_verdict: _ => true over WorkflowRunConclusion counted a Neutral conclusion (no verdict rendered) as a competing verdict: an all-neutral floor refused as NotSuccess instead of Unconcluded, and a neutral run beside a success blocked the entry as contradictory. All eight conclusions are named; two witnesses pin the no-verdict arm (red before, green after). - route_bmc_writes_are_gated: _ => true let a write-shaped StageEffect added later skip the approval-gate audit. ObservationOnly and GitHubControlPlaneWrite are named true-by-arm. - route_dispatch_selector_names_the_attempt: _ => true over QualificationStage inherited the vacuous pass for stages added later. The four non-dispatch stages are named. Fresh-constructor acceptance test: each match now refuses to compile without an arm (NonExhaustiveMatch naming the site, executed on the runner by removing one arm per site). --- dag/gunbc/fleet/fleet_revision_acceptance.dag | 14 ++++++++++-- dag/gunbc/non_fold_residue.dag | 3 --- .../runner_throughput_qualification_route.dag | 14 +++++++++--- ...red_merge_queue_admission_witness_test.dag | 22 ++++++++++++++++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/dag/gunbc/fleet/fleet_revision_acceptance.dag b/dag/gunbc/fleet/fleet_revision_acceptance.dag index b6df59fa7e4..f35736b941f 100644 --- a/dag/gunbc/fleet/fleet_revision_acceptance.dag +++ b/dag/gunbc/fleet/fleet_revision_acceptance.dag @@ -3,7 +3,8 @@ module gunbc.fleet_revision_acceptance import std.types { String, NonEmptyStr, Bool, Int, List } import extdeps.git.object_store { GitObjectId, git_object_id_from_untagged_hex, git_object_id_eq, git_object_id_wire_hex } import extdeps.github.workflow_runs { - WorkflowRun, WorkflowRunList, WorkflowRunConclusion, Success, Cancelled, Skipped, Completed, + WorkflowRun, WorkflowRunList, WorkflowRunConclusion, Success, Failure, Neutral, Cancelled, Skipped, + TimedOut, ActionRequired, StartupFailure, Completed, } import extdeps.github.actions { github_event_name_merge_group } import extdeps.github.push_event { PushRefUpdate } @@ -441,9 +442,18 @@ fn merge_group_run_carries_verdict(run: WorkflowRun) -> Bool { match run.conclusion { Present { value: c } => match c { + // A run carries a verdict when GitHub states a terminal outcome for it: the five + // conclusions below say what happened. Neutral, Cancelled and Skipped report that no + // verdict was rendered -- the run did not judge the revision -- so they are not + // verdicts, and an unconcluded floor refuses as RequiredCiMergeGroupRunUnconcluded. + Success => true + Failure => true + TimedOut => true + ActionRequired => true + StartupFailure => true + Neutral => false Cancelled => false Skipped => false - _ => true } none => false } diff --git a/dag/gunbc/non_fold_residue.dag b/dag/gunbc/non_fold_residue.dag index 054b809d145..64cc3403b26 100644 --- a/dag/gunbc/non_fold_residue.dag +++ b/dag/gunbc/non_fold_residue.dag @@ -784,7 +784,6 @@ data non_fold_residue_frontier: List = [ FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/fleet_health_observe.dag::manager_firmware" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/fleet_health_observe.dag::temperature_sample_of" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/fleet_host_key_enrollment.dag::enrolled_fingerprint_for" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, - FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/fleet_revision_acceptance.dag::merge_group_run_carries_verdict" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/fleet_revision_acceptance.dag::merge_group_run_succeeded" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/fleet_show_effective_read.dag::show_effective_read_from_slice_cap_row" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/fleet/host_credential_custody_converge.dag::converge_host_credential_custody" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, @@ -973,10 +972,8 @@ data non_fold_residue_frontier: List = [ FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification.dag::phase_roster_standing" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification.dag::phase_wall_standing" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification_route.dag::route_bmc_write_count" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, - FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification_route.dag::route_bmc_writes_are_gated" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification_route.dag::route_control_plane_write_count" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification_route.dag::route_deregisters_what_it_registered" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, - FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification_route.dag::route_dispatch_selector_names_the_attempt" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_throughput_qualification_route.dag::route_registers_one_slot" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_unit_live_read.dag::runner_unit_memory_max_drifts_against_cap" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, FrontierRow { subject: PathSubject { path: "dag/gunbc/runner/runner_unit_live_read.dag::runner_unit_swap_max_converged" }, reason: nfr_reason_typed_census_ca5ed1724b, dissolution: nfr_dissolve_owning_fold }, diff --git a/dag/gunbc/runner/runner_throughput_qualification_route.dag b/dag/gunbc/runner/runner_throughput_qualification_route.dag index e52fb479519..a5cb17971b0 100644 --- a/dag/gunbc/runner/runner_throughput_qualification_route.dag +++ b/dag/gunbc/runner/runner_throughput_qualification_route.dag @@ -338,13 +338,16 @@ fn route_control_plane_write_count(route: List) -> Int { ) } -// EVERY BMC WRITE IS UNDER THE APPROVAL GATE, by declaration identity. +// EVERY BMC WRITE IS UNDER THE APPROVAL GATE, by declaration identity. The arms are named, not +// wildcarded: a stage effect added later must state here what the gate audit reads from it, or the +// route stops compiling -- a fresh write-shaped effect may not inherit the audit's pass. fn route_bmc_writes_are_gated(route: List) -> Bool { route |> all(s => match stage_effect(stage: s) { BmcWrite { gate: g, boot: _, image: _ } => (g.module_path as String) == "gunbc.auth.approval_decision_store" && (g.decl_name as String) == "grant_from_store" - _ => true + GitHubControlPlaneWrite { authority: _ } => true + ObservationOnly => true } ) } @@ -384,7 +387,12 @@ fn route_dispatch_selector_names_the_attempt(route: List, at route |> all(s => match s { DispatchFloorToSlot { workload: _, selector: sel, dispatch: _ } => sel |> any(l => l == (attempt as String)) - _ => true + // Named, not wildcarded: only a dispatch stage is asked to name the attempt, but a stage + // added later must state that here rather than inherit the vacuous pass. + BootHostUnderApprovalGate { host: _, boot: _, image: _ } => true + RegisterEphemeralRunnerSlot { unit: _, labels: _, workspace: _, registration: _ } => true + CollectInstruments { instruments: _ } => true + DeregisterRunnerSlot { unit: _, deregistration: _ } => true } ) } diff --git a/dag/test/claim/fleet/fleet_desired_merge_queue_admission_witness_test.dag b/dag/test/claim/fleet/fleet_desired_merge_queue_admission_witness_test.dag index 9f6848fba1c..1319a1e2bf4 100644 --- a/dag/test/claim/fleet/fleet_desired_merge_queue_admission_witness_test.dag +++ b/dag/test/claim/fleet/fleet_desired_merge_queue_admission_witness_test.dag @@ -5,7 +5,7 @@ import std.contract_identity { ContractEpoch } import extdeps.git.object_store { GitObjectId, git_object_id_from_untagged_hex, git_object_id_eq } import extdeps.github.workflow_runs { WorkflowRun, WorkflowRunList, WorkflowRunStatus, WorkflowRunConclusion, - Completed, InProgress, Queued, Success, Failure, Cancelled, TimedOut, + Completed, InProgress, Queued, Success, Failure, Cancelled, Neutral, TimedOut, } import extdeps.github.push_event { PushRefUpdate, push_ref_update_from_event_json, PushRefUpdateObserved, PushRefUpdateMemberAbsent } import gunbc.generated_artifact { WitnessFloorYamlArtifact, artifact_path } @@ -118,6 +118,26 @@ test fn a_cancelled_run_beside_a_success_admits() -> Bool { admits(push: main_push(after: sha_r), rs: runs(rs: [mg(id: 11, conclusion: Cancelled), mg(id: 12, conclusion: Success)])) } +// NO VERDICT RENDERED IS NOT A FAILED VERDICT. A neutral conclusion reports that the workflow did +// not apply to this revision; it carries no verdict, so a floor of only-neutral runs is +// UNCONCLUDED -- not "the floor ran and did not succeed", which is a different refusal and a +// different operator action. merge_group_run_carries_verdict names all eight conclusions so a +// conclusion added later cannot inherit verdict-carrying without an arm. +test fn a_floor_of_neutral_runs_refuses_as_unconcluded_not_not_success() -> Bool { + match ci_on_r(rs: runs(rs: [mg(id: 11, conclusion: Neutral)])) { + Present { value: RequiredCiRefused { cause: RequiredCiMergeGroupRunUnconcluded { revision: _, run_ids: ids } } } => + ids == [11] + _ => false + } +} + +// A neutral run beside a success neither contradicts it nor blocks the entry: not applicable is +// not a competing verdict, and the refusal it caused before the classification named all eight +// conclusions read as two runs disagreeing about one revision. +test fn a_neutral_run_beside_a_success_admits() -> Bool { + admits(push: main_push(after: sha_r), rs: runs(rs: [mg(id: 11, conclusion: Neutral), mg(id: 12, conclusion: Success)])) +} + // THE CONTRADICTION. Nothing picks one: the refusal carries both id sets so an operator reads both logs. test fn a_success_and_a_failure_on_one_revision_refuse_carrying_both_run_ids() -> Bool { match ci_on_r(rs: runs(rs: [mg(id: 11, conclusion: Success), mg(id: 12, conclusion: Failure)])) { From cd906dd1a8ff1e280d8c62dae6537a94c17d7e63 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 11:17:31 +0000 Subject: [PATCH 2/3] Source annotations to module grain The floor's parse refuses '//' inside a declaration body (DESIGN 4c: only leading blocks at module-item grain); move the verdict-classifier and dispatch-gate rationale above their declarations. --- dag/gunbc/fleet/fleet_revision_acceptance.dag | 9 +++++---- .../runner/runner_throughput_qualification_route.dag | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/dag/gunbc/fleet/fleet_revision_acceptance.dag b/dag/gunbc/fleet/fleet_revision_acceptance.dag index f35736b941f..cbab167774c 100644 --- a/dag/gunbc/fleet/fleet_revision_acceptance.dag +++ b/dag/gunbc/fleet/fleet_revision_acceptance.dag @@ -436,16 +436,17 @@ fn run_is_merge_group(run: WorkflowRun) -> Bool { run.event == github_event_name_merge_group } +// A run carries a verdict when GitHub states a terminal outcome for it: Success, Failure, +// TimedOut, ActionRequired and StartupFailure say what happened. Neutral, Cancelled and Skipped +// report that no verdict was rendered -- the run did not judge the revision -- so they are not +// verdicts, and an unconcluded floor refuses as RequiredCiMergeGroupRunUnconcluded. Every +// conclusion is named so a conclusion added later cannot inherit verdict-carrying. fn merge_group_run_carries_verdict(run: WorkflowRun) -> Bool { match run.status { Completed => match run.conclusion { Present { value: c } => match c { - // A run carries a verdict when GitHub states a terminal outcome for it: the five - // conclusions below say what happened. Neutral, Cancelled and Skipped report that no - // verdict was rendered -- the run did not judge the revision -- so they are not - // verdicts, and an unconcluded floor refuses as RequiredCiMergeGroupRunUnconcluded. Success => true Failure => true TimedOut => true diff --git a/dag/gunbc/runner/runner_throughput_qualification_route.dag b/dag/gunbc/runner/runner_throughput_qualification_route.dag index a5cb17971b0..0ba5d4c066a 100644 --- a/dag/gunbc/runner/runner_throughput_qualification_route.dag +++ b/dag/gunbc/runner/runner_throughput_qualification_route.dag @@ -383,12 +383,12 @@ fn route_deregisters_what_it_registered(route: List) -> Bool && (registered |> all(u => deregistered |> any(d => d == u))) } +// Only a dispatch stage is asked to name the attempt; the other four stages say so by name, not +// by wildcard, so a stage added later must state that here rather than inherit the vacuous pass. fn route_dispatch_selector_names_the_attempt(route: List, attempt: NonEmptyStr) -> Bool { route |> all(s => match s { DispatchFloorToSlot { workload: _, selector: sel, dispatch: _ } => sel |> any(l => l == (attempt as String)) - // Named, not wildcarded: only a dispatch stage is asked to name the attempt, but a stage - // added later must state that here rather than inherit the vacuous pass. BootHostUnderApprovalGate { host: _, boot: _, image: _ } => true RegisterEphemeralRunnerSlot { unit: _, labels: _, workspace: _, registration: _ } => true CollectInstruments { instruments: _ } => true From 5d25b04767146adc5c5fc385d7bb1be98d01f302 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 5 Oct 2026 12:47:23 +0000 Subject: [PATCH 3/3] Name the six workflow statuses in the verdict classifier The row I deleted asserted the fn's wildcard residue; the fn still carried one (_ => false over WorkflowRunStatus), so the floor's diff verdict read unrostered=1. Name Queued, InProgress, Waiting, Requested and Pending => false -- a run that has not completed has rendered nothing yet -- so the fn carries no wildcard at all and the deletion is earned. --- dag/gunbc/fleet/fleet_revision_acceptance.dag | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dag/gunbc/fleet/fleet_revision_acceptance.dag b/dag/gunbc/fleet/fleet_revision_acceptance.dag index cbab167774c..7d18cfb865a 100644 --- a/dag/gunbc/fleet/fleet_revision_acceptance.dag +++ b/dag/gunbc/fleet/fleet_revision_acceptance.dag @@ -4,7 +4,7 @@ import std.types { String, NonEmptyStr, Bool, Int, List } import extdeps.git.object_store { GitObjectId, git_object_id_from_untagged_hex, git_object_id_eq, git_object_id_wire_hex } import extdeps.github.workflow_runs { WorkflowRun, WorkflowRunList, WorkflowRunConclusion, Success, Failure, Neutral, Cancelled, Skipped, - TimedOut, ActionRequired, StartupFailure, Completed, + TimedOut, ActionRequired, StartupFailure, Completed, Queued, InProgress, Waiting, Requested, Pending, } import extdeps.github.actions { github_event_name_merge_group } import extdeps.github.push_event { PushRefUpdate } @@ -439,8 +439,10 @@ fn run_is_merge_group(run: WorkflowRun) -> Bool { // A run carries a verdict when GitHub states a terminal outcome for it: Success, Failure, // TimedOut, ActionRequired and StartupFailure say what happened. Neutral, Cancelled and Skipped // report that no verdict was rendered -- the run did not judge the revision -- so they are not -// verdicts, and an unconcluded floor refuses as RequiredCiMergeGroupRunUnconcluded. Every -// conclusion is named so a conclusion added later cannot inherit verdict-carrying. +// verdicts, and an unconcluded floor refuses as RequiredCiMergeGroupRunUnconcluded. A run that +// has not completed (Queued, InProgress, Waiting, Requested, Pending) has rendered nothing yet. +// Every status and every conclusion is named so an addition to either cannot inherit +// verdict-carrying without an arm. fn merge_group_run_carries_verdict(run: WorkflowRun) -> Bool { match run.status { Completed => @@ -458,7 +460,11 @@ fn merge_group_run_carries_verdict(run: WorkflowRun) -> Bool { } none => false } - _ => false + Queued => false + InProgress => false + Waiting => false + Requested => false + Pending => false } }