Skip to main content

dpm_engine/gates/
provisional.rs

1//! Provisional start releases and derived basis invalidation, evaluated inside the shared gate
2//! evaluator so every view and command reads the same result.
3
4use super::UnmetGate;
5use dpm_model::{BasisState, DependencyPolicy, Endpoint, Plan, Release, WorkItem, basis_status};
6
7/// One gate per enforced edge whose effective basis names a rejected predecessor attempt.
8pub(super) fn basis_gates(plan: &Plan, work: &WorkItem) -> Vec<UnmetGate> {
9    basis_status(plan, work)
10        .into_iter()
11        .filter(dpm_model::BasisStatus::gates)
12        .map(|status| UnmetGate::BasisInvalidated {
13            dependency: status.dependency,
14            predecessor: status.predecessor,
15            key: status.predecessor_key,
16            attempt: status.basis.attempt,
17            state: status.state,
18            current_attempt: status.current_attempt,
19        })
20        .collect()
21}
22
23impl super::ProvisionalRelease {
24    /// Human-readable reliance on an unverified attempt, shared by `explain` and `next`.
25    #[must_use]
26    pub fn describe(&self) -> String {
27        format!(
28            "provisional: {} attempt #{} is only submitted; a start relies on it, and verification still requires its verified finish",
29            self.key, self.attempt
30        )
31    }
32}
33
34pub(super) fn describe_start(
35    key: &str,
36    release: Release,
37    attempt: Option<u32>,
38    edge: &str,
39    policy: DependencyPolicy,
40) -> String {
41    match (release, attempt) {
42        (Release::Elapsing { event_at, opens_at }, Some(n)) => format!(
43            "{key} attempt #{n} submitted at {event_at}; provisional lag elapses at {opens_at} ({edge})"
44        ),
45        (Release::LagOutOfRange { event_at }, Some(n)) => format!(
46            "{key} attempt #{n} submitted at {event_at}, but the lag exceeds the supported time range ({edge})"
47        ),
48        (Release::AwaitingEvent, _) => format!(
49            "awaits a pending submission attempt or the verified finish of {key} ({edge}, provisional start)"
50        ),
51        _ => super::transition::describe_release(key, Endpoint::Finish, release, edge, policy),
52    }
53}
54
55pub(super) fn describe_invalidated(
56    key: &str,
57    attempt: u32,
58    state: &BasisState,
59    current: Option<u32>,
60) -> String {
61    let rejection = match state {
62        BasisState::Invalidated {
63            rejected_by,
64            rejected_at,
65            reason,
66        } => format!(" by {rejected_by} at {rejected_at}: {reason}"),
67        BasisState::Pending | BasisState::Verified => String::new(),
68    };
69    let remedy = match current {
70        Some(n) => format!(
71            "an independent human or service may revalidate this work against {key} attempt #{n}"
72        ),
73        None => format!(
74            "{key} has no pending or verified attempt yet; revalidation waits for its resubmission"
75        ),
76    };
77    format!(
78        "execution relies on {key} attempt #{attempt}, which was rejected{rejection}; a later submission or verification never validates it silently: {remedy}"
79    )
80}