Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions dag/gunbc/fleet/fleet_revision_acceptance.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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, Queued, InProgress, Waiting, Requested, Pending,
}
import extdeps.github.actions { github_event_name_merge_group }
import extdeps.github.push_event { PushRefUpdate }
Expand Down Expand Up @@ -435,20 +436,27 @@ 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. 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 =>
match run.conclusion {
Present { value: c } =>
match c {
Cancelled => false
Skipped => false
Success => true
Failure => true
Neutral => true
TimedOut => true
ActionRequired => true
StartupFailure => true
Neutral => false
Cancelled => false
Skipped => false
}
none => false
}
Expand All @@ -465,13 +473,7 @@ fn merge_group_run_succeeded(run: WorkflowRun) -> Bool {
Present { value: c } =>
match c {
Success => true
Failure => false
Cancelled => false
Neutral => false
Skipped => false
TimedOut => false
ActionRequired => false
StartupFailure => false
_ => false
}
none => false
}
Expand Down
6 changes: 5 additions & 1 deletion dag/gunbc/runner/runner_throughput_qualification_route.dag
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ fn route_control_plane_write_count(route: List<QualificationStage>) -> 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<QualificationStage>) -> Bool {
route |> all(s =>
match stage_effect(stage: s) {
Expand Down Expand Up @@ -392,6 +394,8 @@ fn route_deregisters_what_it_registered(route: List<QualificationStage>) -> 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<QualificationStage>, attempt: NonEmptyStr) -> Bool {
route |> all(s =>
match s {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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)])) {
Expand Down
Loading