use super::OptionValueExt;
use meerkat_machine_dsl::machine;
#[derive(
Debug,
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
pub struct WorkItemKey(pub String);
impl<T: Into<String>> From<T> for WorkItemKey {
fn from(value: T) -> Self {
Self(value.into())
}
}
#[derive(
Debug,
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
pub struct WorkEdgeKey(pub String);
impl<T: Into<String>> From<T> for WorkEdgeKey {
fn from(value: T) -> Self {
Self(value.into())
}
}
#[derive(
Debug,
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
pub struct WorkDependencyPathKey(pub String);
impl<T: Into<String>> From<T> for WorkDependencyPathKey {
fn from(value: T) -> Self {
Self(value.into())
}
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkOwnerKind {
Principal,
Agent,
Session,
Mob,
#[default]
Label,
}
#[derive(
Debug,
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
pub struct WorkOwnerKey {
pub kind: WorkOwnerKind,
pub id: String,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkEdgeKind {
#[default]
Blocks,
Parent,
Related,
Supersedes,
DerivedFrom,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkCompletionPolicy {
#[default]
SelfAttest,
HostConfirmed,
PrincipalConfirmed,
Supervisor,
ReviewerQuorum,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkEvidenceKind {
#[default]
SelfAttest,
HostConfirmation,
PrincipalConfirmation,
SupervisorConfirmation,
ReviewerConfirmation,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkGraphErrorKind {
#[default]
NotFound,
AttentionNotFound,
StaleRevision,
Conflict,
InvalidTransition,
InvalidInput,
InvalidTimestampMillis,
Store,
UnsupportedBackend,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkGraphPublicErrorClass {
#[default]
NotFound,
Conflict,
InvalidTransition,
InvalidArguments,
CapabilityUnavailable,
StoreError,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkCreateStatusAdmissionKind {
#[default]
Denied,
AdmittedOpen,
AdmittedBlocked,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkCreateCompletionPolicyAdmissionKind {
#[default]
DeniedNonSelfAttest,
Admitted,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkCloseStatusAdmissionKind {
#[default]
DeniedNonTerminal,
AdmittedCompleted,
AdmittedCancelled,
AdmittedFailed,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkPublicConfirmationAdmissionKind {
#[default]
DeniedRequiresTrustedHost,
Admitted,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkCompletionPolicyMutationAdmissionKind {
#[default]
Denied,
Admitted,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkPolicyEscalationAdmissionKind {
#[default]
Denied,
Admitted,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkConfirmationEvidenceObservation {
#[default]
Empty,
Other,
HostConfirmation,
PrincipalConfirmation,
SupervisorConfirmation,
ReviewerConfirmation,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[serde(rename_all = "snake_case")]
pub enum WorkConfirmationAdmissionKind {
#[default]
DeniedPrincipalRequired,
DeniedPrincipalKindMismatch,
DeniedSupervisorMismatch,
DeniedEvidenceKind,
DeniedSelfAttestEmptyEvidenceKind,
Admitted,
}
machine! {
machine WorkGraphLifecycleMachine {
version: 1,
rust: "self" / "catalog::dsl::workgraph_lifecycle",
state {
lifecycle_phase: WorkLifecycleState,
revision: u64,
unresolved_blocker_count: u64,
topology_item_keys: Set<WorkItemKey>,
topology_edge_keys: Set<WorkEdgeKey>,
blocks_reachability: Set<WorkDependencyPathKey>,
parent_reachability: Set<WorkDependencyPathKey>,
claim_owner_key: Option<WorkOwnerKey>,
claimed_at_utc_ms: Option<u64>,
lease_expires_at_utc_ms: Option<u64>,
due_at_utc_ms: Option<u64>,
not_before_utc_ms: Option<u64>,
snoozed_until_utc_ms: Option<u64>,
completion_policy: Enum<WorkCompletionPolicy>,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
completion_reviewer_quorum_threshold: Option<u64>,
terminal_at_utc_ms: Option<u64>,
evidence_count: u64,
host_confirmation_count: u64,
principal_confirmation_count: u64,
supervisor_confirmation_owner_keys: Set<WorkOwnerKey>,
reviewer_confirmation_owner_keys: Set<WorkOwnerKey>,
}
init(Absent) {
revision = 0,
unresolved_blocker_count = 0,
topology_item_keys = EmptySet,
topology_edge_keys = EmptySet,
blocks_reachability = EmptySet,
parent_reachability = EmptySet,
claim_owner_key = None,
claimed_at_utc_ms = None,
lease_expires_at_utc_ms = None,
due_at_utc_ms = None,
not_before_utc_ms = None,
snoozed_until_utc_ms = None,
completion_policy = WorkCompletionPolicy::SelfAttest,
completion_supervisor_owner_key = None,
completion_reviewer_quorum_threshold = None,
terminal_at_utc_ms = None,
evidence_count = 0,
host_confirmation_count = 0,
principal_confirmation_count = 0,
supervisor_confirmation_owner_keys = EmptySet,
reviewer_confirmation_owner_keys = EmptySet,
}
terminal [Completed, Cancelled, Failed]
phase WorkLifecycleState {
Absent,
Open,
InProgress,
Blocked,
Completed,
Cancelled,
Failed,
}
input WorkGraphLifecycleInput {
CreateOpen {
due_at_utc_ms: Option<u64>,
not_before_utc_ms: Option<u64>,
snoozed_until_utc_ms: Option<u64>,
completion_policy: Enum<WorkCompletionPolicy>,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
completion_reviewer_quorum_threshold: Option<u64>,
unresolved_blocker_count: u64,
},
CreateBlocked {
due_at_utc_ms: Option<u64>,
not_before_utc_ms: Option<u64>,
snoozed_until_utc_ms: Option<u64>,
completion_policy: Enum<WorkCompletionPolicy>,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
completion_reviewer_quorum_threshold: Option<u64>,
unresolved_blocker_count: u64,
},
Update {
expected_revision: u64,
due_at_utc_ms: Option<u64>,
not_before_utc_ms: Option<u64>,
snoozed_until_utc_ms: Option<u64>,
completion_policy: Enum<WorkCompletionPolicy>,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
completion_reviewer_quorum_threshold: Option<u64>,
unresolved_blocker_count: u64,
},
PolicyEscalate {
expected_revision: u64,
requested_completion_policy: Enum<WorkCompletionPolicy>,
requested_completion_supervisor_owner_key: Option<WorkOwnerKey>,
requested_completion_reviewer_quorum_threshold: Option<u64>,
},
Claim {
expected_revision: u64,
owner_key: WorkOwnerKey,
now_utc_ms: u64,
lease_expires_at_utc_ms: Option<u64>,
},
Release { expected_revision: u64 },
Block { expected_revision: u64 },
RefreshEligibility { unresolved_blocker_count: u64 },
ValidateLink {
kind: WorkEdgeKind,
from_item_key: WorkItemKey,
to_item_key: WorkItemKey,
edge_key: WorkEdgeKey,
reverse_path_key: WorkDependencyPathKey,
},
CloseCompleted { expected_revision: u64, at_utc_ms: u64 },
CloseCancelled { expected_revision: u64, at_utc_ms: u64 },
CloseFailed { expected_revision: u64, at_utc_ms: u64 },
AddEvidence {
expected_revision: u64,
evidence_kind: Enum<WorkEvidenceKind>,
confirming_owner_key: Option<WorkOwnerKey>,
},
ClassifyWorkGraphPublicError { kind: Enum<WorkGraphErrorKind> },
ClassifyTerminality {},
ClassifyBlockerSatisfied {
blocker_present: bool,
blocker_lifecycle_phase: Enum<WorkLifecycleState>,
},
ClassifyCreateStatusAdmission { requested_status: Enum<WorkLifecycleState> },
ClassifyCreateCompletionPolicyAdmission { completion_policy: Enum<WorkCompletionPolicy> },
ClassifyCloseStatusAdmission { requested_status: Enum<WorkLifecycleState> },
ClassifyPublicConfirmationAdmission { completion_policy: Enum<WorkCompletionPolicy> },
ClassifyCompletionPolicyMutationAdmission {
requested_completion_policy: Enum<WorkCompletionPolicy>,
requested_completion_supervisor_owner_key: Option<WorkOwnerKey>,
requested_completion_reviewer_quorum_threshold: Option<u64>,
},
ClassifyConfirmationAdmission {
completion_policy: Enum<WorkCompletionPolicy>,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
requested_principal_owner_key: Option<WorkOwnerKey>,
requested_principal_kind: Option<Enum<WorkOwnerKind>>,
supplied_evidence_kind: Enum<WorkConfirmationEvidenceObservation>,
},
ClassifyReadiness { now_utc_ms: u64 },
}
effect WorkGraphLifecycleEffect {
Created,
Updated,
Claimed { owner_key: WorkOwnerKey },
Released,
Blocked,
LinkValidated,
Closed { terminal_state: WorkLifecycleState, at_utc_ms: u64 },
EvidenceAdded,
WorkGraphPublicErrorClassified {
kind: Enum<WorkGraphErrorKind>,
public_class: Enum<WorkGraphPublicErrorClass>,
},
WorkItemTerminalityClassified { terminal: bool },
BlockerSatisfactionClassified { satisfied: bool },
CreateStatusAdmissionClassified { admission: Enum<WorkCreateStatusAdmissionKind> },
CreateCompletionPolicyAdmissionClassified {
admission: Enum<WorkCreateCompletionPolicyAdmissionKind>,
},
CloseStatusAdmissionClassified { admission: Enum<WorkCloseStatusAdmissionKind> },
PublicConfirmationAdmissionClassified {
admission: Enum<WorkPublicConfirmationAdmissionKind>,
},
CompletionPolicyMutationAdmissionClassified {
admission: Enum<WorkCompletionPolicyMutationAdmissionKind>,
},
PolicyEscalationAdmissionClassified {
admission: Enum<WorkPolicyEscalationAdmissionKind>,
},
ConfirmationAdmissionClassified {
admission: Enum<WorkConfirmationAdmissionKind>,
},
WorkItemReadinessClassified { ready: bool },
}
invariant absent_has_zero_revision {
self.lifecycle_phase != Phase::Absent || self.revision == 0
}
invariant live_has_positive_revision {
self.lifecycle_phase == Phase::Absent || self.revision > 0
}
invariant topology_snapshot_is_stateless {
self.topology_item_keys == EmptySet
|| self.topology_edge_keys == EmptySet
|| self.lifecycle_phase == Phase::Absent
}
invariant terminal_has_terminal_time {
(self.lifecycle_phase != Phase::Completed && self.lifecycle_phase != Phase::Cancelled && self.lifecycle_phase != Phase::Failed)
|| self.terminal_at_utc_ms != None
}
invariant claim_only_in_progress {
self.claim_owner_key == None || self.lifecycle_phase == Phase::InProgress
}
invariant blocked_has_no_claim {
self.lifecycle_phase != Phase::Blocked || self.claim_owner_key == None
}
invariant terminal_has_no_claim {
(self.lifecycle_phase != Phase::Completed && self.lifecycle_phase != Phase::Cancelled && self.lifecycle_phase != Phase::Failed)
|| self.claim_owner_key == None
}
helper completion_policy_payload_valid(
policy: WorkCompletionPolicy,
supervisor_owner_key: Option<WorkOwnerKey>,
reviewer_quorum_threshold: Option<u64>
) -> bool {
if policy == WorkCompletionPolicy::Supervisor {
supervisor_owner_key != None && reviewer_quorum_threshold == None
} else {
if policy == WorkCompletionPolicy::ReviewerQuorum {
supervisor_owner_key == None
&& reviewer_quorum_threshold != None
&& reviewer_quorum_threshold.get("value") > 0
} else {
supervisor_owner_key == None && reviewer_quorum_threshold == None
}
}
}
helper completion_policy_is_satisfied(
policy: WorkCompletionPolicy,
supervisor_owner_key: Option<WorkOwnerKey>,
reviewer_quorum_threshold: Option<u64>,
host_confirmation_count: u64,
principal_confirmation_count: u64,
supervisor_confirmation_owner_keys: Set<WorkOwnerKey>,
reviewer_confirmation_owner_keys: Set<WorkOwnerKey>
) -> bool {
if policy == WorkCompletionPolicy::SelfAttest {
true
} else {
if policy == WorkCompletionPolicy::HostConfirmed {
host_confirmation_count > 0
} else {
if policy == WorkCompletionPolicy::PrincipalConfirmed {
principal_confirmation_count > 0
} else {
if policy == WorkCompletionPolicy::Supervisor {
supervisor_owner_key != None
&& supervisor_confirmation_owner_keys.contains(supervisor_owner_key.get("value"))
} else {
reviewer_quorum_threshold != None
&& reviewer_confirmation_owner_keys.len() >= reviewer_quorum_threshold.get("value")
}
}
}
}
}
helper completion_policy_escalation_admissible(
current_policy: WorkCompletionPolicy,
current_reviewer_quorum_threshold: Option<u64>,
requested_policy: WorkCompletionPolicy,
requested_supervisor_owner_key: Option<WorkOwnerKey>,
requested_reviewer_quorum_threshold: Option<u64>
) -> bool {
completion_policy_payload_valid(
requested_policy,
requested_supervisor_owner_key,
requested_reviewer_quorum_threshold
)
&& (
(
current_policy == WorkCompletionPolicy::SelfAttest
&& requested_policy != WorkCompletionPolicy::SelfAttest
)
|| (
current_policy == WorkCompletionPolicy::ReviewerQuorum
&& requested_policy == WorkCompletionPolicy::ReviewerQuorum
&& current_reviewer_quorum_threshold != None
&& requested_reviewer_quorum_threshold != None
&& requested_reviewer_quorum_threshold.get("value")
> current_reviewer_quorum_threshold.get("value")
)
)
}
helper evidence_kind_owner_key_present(
evidence_kind: WorkEvidenceKind,
confirming_owner_key: Option<WorkOwnerKey>
) -> bool {
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
confirming_owner_key != None
} else {
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
confirming_owner_key != None
} else {
true
}
}
}
helper confirmation_denies_principal_required(
completion_policy: WorkCompletionPolicy,
requested_principal_owner_key: Option<WorkOwnerKey>
) -> bool {
(completion_policy == WorkCompletionPolicy::PrincipalConfirmed
|| completion_policy == WorkCompletionPolicy::Supervisor
|| completion_policy == WorkCompletionPolicy::ReviewerQuorum)
&& requested_principal_owner_key == None
}
helper confirmation_denies_principal_kind_mismatch(
completion_policy: WorkCompletionPolicy,
requested_principal_owner_key: Option<WorkOwnerKey>,
requested_principal_kind: Option<WorkOwnerKind>
) -> bool {
completion_policy == WorkCompletionPolicy::PrincipalConfirmed
&& requested_principal_owner_key != None
&& (requested_principal_kind == None
|| requested_principal_kind.get("value") != WorkOwnerKind::Principal)
}
helper confirmation_denies_supervisor_mismatch(
completion_policy: WorkCompletionPolicy,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
requested_principal_owner_key: Option<WorkOwnerKey>
) -> bool {
completion_policy == WorkCompletionPolicy::Supervisor
&& requested_principal_owner_key != None
&& (completion_supervisor_owner_key == None
|| requested_principal_owner_key.get("value")
!= completion_supervisor_owner_key.get("value"))
}
helper confirmation_denies_self_attest_empty(
completion_policy: WorkCompletionPolicy,
supplied_evidence_kind: WorkConfirmationEvidenceObservation
) -> bool {
completion_policy == WorkCompletionPolicy::SelfAttest
&& supplied_evidence_kind == WorkConfirmationEvidenceObservation::Empty
}
helper confirmation_denies_evidence_kind(
completion_policy: WorkCompletionPolicy,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
requested_principal_owner_key: Option<WorkOwnerKey>,
requested_principal_kind: Option<WorkOwnerKind>,
supplied_evidence_kind: WorkConfirmationEvidenceObservation
) -> bool {
if completion_policy == WorkCompletionPolicy::HostConfirmed {
supplied_evidence_kind != WorkConfirmationEvidenceObservation::HostConfirmation
} else {
if completion_policy == WorkCompletionPolicy::PrincipalConfirmed {
confirmation_denies_principal_required(completion_policy, requested_principal_owner_key) == false
&& confirmation_denies_principal_kind_mismatch(completion_policy, requested_principal_owner_key, requested_principal_kind) == false
&& supplied_evidence_kind != WorkConfirmationEvidenceObservation::PrincipalConfirmation
} else {
if completion_policy == WorkCompletionPolicy::Supervisor {
confirmation_denies_principal_required(completion_policy, requested_principal_owner_key) == false
&& confirmation_denies_supervisor_mismatch(completion_policy, completion_supervisor_owner_key, requested_principal_owner_key) == false
&& supplied_evidence_kind != WorkConfirmationEvidenceObservation::SupervisorConfirmation
} else {
if completion_policy == WorkCompletionPolicy::ReviewerQuorum {
confirmation_denies_principal_required(completion_policy, requested_principal_owner_key) == false
&& supplied_evidence_kind != WorkConfirmationEvidenceObservation::ReviewerConfirmation
} else {
false
}
}
}
}
}
helper confirmation_admits(
completion_policy: WorkCompletionPolicy,
completion_supervisor_owner_key: Option<WorkOwnerKey>,
requested_principal_owner_key: Option<WorkOwnerKey>,
requested_principal_kind: Option<WorkOwnerKind>,
supplied_evidence_kind: WorkConfirmationEvidenceObservation
) -> bool {
confirmation_denies_principal_required(completion_policy, requested_principal_owner_key) == false
&& confirmation_denies_principal_kind_mismatch(completion_policy, requested_principal_owner_key, requested_principal_kind) == false
&& confirmation_denies_supervisor_mismatch(completion_policy, completion_supervisor_owner_key, requested_principal_owner_key) == false
&& confirmation_denies_self_attest_empty(completion_policy, supplied_evidence_kind) == false
&& confirmation_denies_evidence_kind(completion_policy, completion_supervisor_owner_key, requested_principal_owner_key, requested_principal_kind, supplied_evidence_kind) == false
}
invariant supervisor_policy_has_owner {
self.completion_policy != WorkCompletionPolicy::Supervisor
|| self.completion_supervisor_owner_key != None
}
invariant non_supervisor_policy_has_no_owner {
self.completion_policy == WorkCompletionPolicy::Supervisor
|| self.completion_supervisor_owner_key == None
}
invariant reviewer_quorum_policy_has_positive_threshold {
self.completion_policy != WorkCompletionPolicy::ReviewerQuorum
|| (self.completion_reviewer_quorum_threshold != None
&& self.completion_reviewer_quorum_threshold.get("value") > 0)
}
invariant non_reviewer_quorum_policy_has_no_threshold {
self.completion_policy == WorkCompletionPolicy::ReviewerQuorum
|| self.completion_reviewer_quorum_threshold == None
}
disposition Created => local seam NoOwnerRealization,
disposition Updated => local seam NoOwnerRealization,
disposition Claimed => local seam NoOwnerRealization,
disposition Released => local seam NoOwnerRealization,
disposition Blocked => local seam NoOwnerRealization,
disposition LinkValidated => local seam NoOwnerRealization,
disposition Closed => routed [WorkAttentionLifecycleMachine] seam NoOwnerRealization,
disposition EvidenceAdded => local seam NoOwnerRealization,
disposition WorkGraphPublicErrorClassified => local seam SurfaceResultAlignment,
disposition WorkItemTerminalityClassified => local seam SurfaceResultAlignment,
disposition BlockerSatisfactionClassified => local seam SurfaceResultAlignment,
disposition CreateStatusAdmissionClassified => local seam SurfaceResultAlignment,
disposition CreateCompletionPolicyAdmissionClassified => local seam SurfaceResultAlignment,
disposition CloseStatusAdmissionClassified => local seam SurfaceResultAlignment,
disposition PublicConfirmationAdmissionClassified => local seam SurfaceResultAlignment,
disposition CompletionPolicyMutationAdmissionClassified => local seam SurfaceResultAlignment,
disposition PolicyEscalationAdmissionClassified => local seam SurfaceResultAlignment,
disposition ConfirmationAdmissionClassified => local seam SurfaceResultAlignment,
disposition WorkItemReadinessClassified => local seam SurfaceResultAlignment,
transition CreateOpen {
on input CreateOpen { due_at_utc_ms, not_before_utc_ms, snoozed_until_utc_ms, completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold, unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::Absent }
guard "completion_policy_payload_valid" {
completion_policy_payload_valid(completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold)
}
update {
self.revision = 1;
self.unresolved_blocker_count = unresolved_blocker_count;
self.due_at_utc_ms = due_at_utc_ms;
self.not_before_utc_ms = not_before_utc_ms;
self.snoozed_until_utc_ms = snoozed_until_utc_ms;
self.completion_policy = completion_policy;
self.completion_supervisor_owner_key = completion_supervisor_owner_key;
self.completion_reviewer_quorum_threshold = completion_reviewer_quorum_threshold;
}
to Open
emit Created
}
transition CreateBlocked {
on input CreateBlocked { due_at_utc_ms, not_before_utc_ms, snoozed_until_utc_ms, completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold, unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::Absent }
guard "completion_policy_payload_valid" {
completion_policy_payload_valid(completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold)
}
update {
self.revision = 1;
self.unresolved_blocker_count = unresolved_blocker_count;
self.due_at_utc_ms = due_at_utc_ms;
self.not_before_utc_ms = not_before_utc_ms;
self.snoozed_until_utc_ms = snoozed_until_utc_ms;
self.completion_policy = completion_policy;
self.completion_supervisor_owner_key = completion_supervisor_owner_key;
self.completion_reviewer_quorum_threshold = completion_reviewer_quorum_threshold;
}
to Blocked
emit Created
}
transition UpdateOpen {
on input Update { expected_revision, due_at_utc_ms, not_before_utc_ms, snoozed_until_utc_ms, completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold, unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
guard "completion_policy_payload_valid" {
completion_policy_payload_valid(completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold)
}
guard "completion_policy_unchanged" {
completion_policy == self.completion_policy
&& completion_supervisor_owner_key == self.completion_supervisor_owner_key
&& completion_reviewer_quorum_threshold == self.completion_reviewer_quorum_threshold
}
update {
self.revision += 1;
self.unresolved_blocker_count = unresolved_blocker_count;
self.due_at_utc_ms = due_at_utc_ms;
self.not_before_utc_ms = not_before_utc_ms;
self.snoozed_until_utc_ms = snoozed_until_utc_ms;
}
to Open
emit Updated
}
transition UpdateInProgress {
on input Update { expected_revision, due_at_utc_ms, not_before_utc_ms, snoozed_until_utc_ms, completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold, unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
guard "completion_policy_payload_valid" {
completion_policy_payload_valid(completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold)
}
guard "completion_policy_unchanged" {
completion_policy == self.completion_policy
&& completion_supervisor_owner_key == self.completion_supervisor_owner_key
&& completion_reviewer_quorum_threshold == self.completion_reviewer_quorum_threshold
}
update {
self.revision += 1;
self.unresolved_blocker_count = unresolved_blocker_count;
self.due_at_utc_ms = due_at_utc_ms;
self.not_before_utc_ms = not_before_utc_ms;
self.snoozed_until_utc_ms = snoozed_until_utc_ms;
}
to InProgress
emit Updated
}
transition UpdateBlocked {
on input Update { expected_revision, due_at_utc_ms, not_before_utc_ms, snoozed_until_utc_ms, completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold, unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
guard "completion_policy_payload_valid" {
completion_policy_payload_valid(completion_policy, completion_supervisor_owner_key, completion_reviewer_quorum_threshold)
}
guard "completion_policy_unchanged" {
completion_policy == self.completion_policy
&& completion_supervisor_owner_key == self.completion_supervisor_owner_key
&& completion_reviewer_quorum_threshold == self.completion_reviewer_quorum_threshold
}
update {
self.revision += 1;
self.unresolved_blocker_count = unresolved_blocker_count;
self.due_at_utc_ms = due_at_utc_ms;
self.not_before_utc_ms = not_before_utc_ms;
self.snoozed_until_utc_ms = snoozed_until_utc_ms;
}
to Blocked
emit Updated
}
transition PolicyEscalateOpenAdmitted {
on input PolicyEscalate { expected_revision, requested_completion_policy, requested_completion_supervisor_owner_key, requested_completion_reviewer_quorum_threshold }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
guard "completion_policy_escalation_admissible" {
completion_policy_escalation_admissible(
self.completion_policy,
self.completion_reviewer_quorum_threshold,
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
)
}
update {
self.revision += 1;
self.completion_policy = requested_completion_policy;
self.completion_supervisor_owner_key = requested_completion_supervisor_owner_key;
self.completion_reviewer_quorum_threshold = requested_completion_reviewer_quorum_threshold;
}
to Open
emit Updated
emit PolicyEscalationAdmissionClassified { admission: WorkPolicyEscalationAdmissionKind::Admitted }
}
transition PolicyEscalateOpenDenied {
on input PolicyEscalate { expected_revision, requested_completion_policy, requested_completion_supervisor_owner_key, requested_completion_reviewer_quorum_threshold }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
guard "completion_policy_escalation_denied" {
completion_policy_escalation_admissible(
self.completion_policy,
self.completion_reviewer_quorum_threshold,
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
) == false
}
update {}
to Open
emit PolicyEscalationAdmissionClassified { admission: WorkPolicyEscalationAdmissionKind::Denied }
}
transition PolicyEscalateInProgressAdmitted {
on input PolicyEscalate { expected_revision, requested_completion_policy, requested_completion_supervisor_owner_key, requested_completion_reviewer_quorum_threshold }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
guard "completion_policy_escalation_admissible" {
completion_policy_escalation_admissible(
self.completion_policy,
self.completion_reviewer_quorum_threshold,
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
)
}
update {
self.revision += 1;
self.completion_policy = requested_completion_policy;
self.completion_supervisor_owner_key = requested_completion_supervisor_owner_key;
self.completion_reviewer_quorum_threshold = requested_completion_reviewer_quorum_threshold;
}
to InProgress
emit Updated
emit PolicyEscalationAdmissionClassified { admission: WorkPolicyEscalationAdmissionKind::Admitted }
}
transition PolicyEscalateInProgressDenied {
on input PolicyEscalate { expected_revision, requested_completion_policy, requested_completion_supervisor_owner_key, requested_completion_reviewer_quorum_threshold }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
guard "completion_policy_escalation_denied" {
completion_policy_escalation_admissible(
self.completion_policy,
self.completion_reviewer_quorum_threshold,
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
) == false
}
update {}
to InProgress
emit PolicyEscalationAdmissionClassified { admission: WorkPolicyEscalationAdmissionKind::Denied }
}
transition PolicyEscalateBlockedAdmitted {
on input PolicyEscalate { expected_revision, requested_completion_policy, requested_completion_supervisor_owner_key, requested_completion_reviewer_quorum_threshold }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
guard "completion_policy_escalation_admissible" {
completion_policy_escalation_admissible(
self.completion_policy,
self.completion_reviewer_quorum_threshold,
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
)
}
update {
self.revision += 1;
self.completion_policy = requested_completion_policy;
self.completion_supervisor_owner_key = requested_completion_supervisor_owner_key;
self.completion_reviewer_quorum_threshold = requested_completion_reviewer_quorum_threshold;
}
to Blocked
emit Updated
emit PolicyEscalationAdmissionClassified { admission: WorkPolicyEscalationAdmissionKind::Admitted }
}
transition PolicyEscalateBlockedDenied {
on input PolicyEscalate { expected_revision, requested_completion_policy, requested_completion_supervisor_owner_key, requested_completion_reviewer_quorum_threshold }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
guard "completion_policy_escalation_denied" {
completion_policy_escalation_admissible(
self.completion_policy,
self.completion_reviewer_quorum_threshold,
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
) == false
}
update {}
to Blocked
emit PolicyEscalationAdmissionClassified { admission: WorkPolicyEscalationAdmissionKind::Denied }
}
transition ClaimOpen {
on input Claim { expected_revision, owner_key, now_utc_ms, lease_expires_at_utc_ms }
guard "revision_matches" { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
guard "dependencies_satisfied" { self.unresolved_blocker_count == 0 }
guard "due_eligible" { if self.due_at_utc_ms == None { true } else { self.due_at_utc_ms.get("value") <= now_utc_ms } }
guard "not_before_eligible" { if self.not_before_utc_ms == None { true } else { self.not_before_utc_ms.get("value") <= now_utc_ms } }
guard "snooze_eligible" { if self.snoozed_until_utc_ms == None { true } else { self.snoozed_until_utc_ms.get("value") <= now_utc_ms } }
update {
self.revision += 1;
self.claim_owner_key = Some(owner_key);
self.claimed_at_utc_ms = Some(now_utc_ms);
self.lease_expires_at_utc_ms = lease_expires_at_utc_ms;
}
to InProgress
emit Claimed { owner_key: owner_key }
}
transition ClaimExpiredInProgress {
on input Claim { expected_revision, owner_key, now_utc_ms, lease_expires_at_utc_ms }
guard "revision_matches" { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
guard "prior_claim_present" { self.claim_owner_key != None }
guard "prior_claim_has_lease" { self.lease_expires_at_utc_ms != None }
guard "prior_claim_expired" { if self.lease_expires_at_utc_ms == None { false } else { self.lease_expires_at_utc_ms.get("value") <= now_utc_ms } }
guard "dependencies_satisfied" { self.unresolved_blocker_count == 0 }
guard "due_eligible" { if self.due_at_utc_ms == None { true } else { self.due_at_utc_ms.get("value") <= now_utc_ms } }
guard "not_before_eligible" { if self.not_before_utc_ms == None { true } else { self.not_before_utc_ms.get("value") <= now_utc_ms } }
guard "snooze_eligible" { if self.snoozed_until_utc_ms == None { true } else { self.snoozed_until_utc_ms.get("value") <= now_utc_ms } }
update {
self.revision += 1;
self.claim_owner_key = Some(owner_key);
self.claimed_at_utc_ms = Some(now_utc_ms);
self.lease_expires_at_utc_ms = lease_expires_at_utc_ms;
}
to InProgress
emit Claimed { owner_key: owner_key }
}
transition ReleaseInProgress {
on input Release { expected_revision }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision && self.claim_owner_key != None }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
}
to Open
emit Released
}
transition BlockOpen {
on input Block { expected_revision }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
}
to Blocked
emit Blocked
}
transition BlockInProgress {
on input Block { expected_revision }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
}
to Blocked
emit Blocked
}
transition BlockBlocked {
on input Block { expected_revision }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
}
to Blocked
emit Blocked
}
transition RefreshEligibilityOpen {
on input RefreshEligibility { unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::Open }
update {
self.revision += 1;
self.unresolved_blocker_count = unresolved_blocker_count;
}
to Open
}
transition RefreshEligibilityInProgress {
on input RefreshEligibility { unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::InProgress }
update {
self.revision += 1;
self.unresolved_blocker_count = unresolved_blocker_count;
}
to InProgress
}
transition RefreshEligibilityBlocked {
on input RefreshEligibility { unresolved_blocker_count }
guard { self.lifecycle_phase == Phase::Blocked }
update {
self.revision += 1;
self.unresolved_blocker_count = unresolved_blocker_count;
}
to Blocked
}
transition ValidateLink {
on input ValidateLink { kind, from_item_key, to_item_key, edge_key, reverse_path_key }
guard "stateless_checker" { self.lifecycle_phase == Phase::Absent }
guard "from_endpoint_exists" { self.topology_item_keys.contains(from_item_key) }
guard "to_endpoint_exists" { self.topology_item_keys.contains(to_item_key) }
guard "not_self_edge" { from_item_key != to_item_key }
guard "not_duplicate_edge" { self.topology_edge_keys.contains(edge_key) == false }
guard "blocks_acyclic" {
kind != WorkEdgeKind::Blocks || self.blocks_reachability.contains(reverse_path_key) == false
}
guard "parent_acyclic" {
kind != WorkEdgeKind::Parent || self.parent_reachability.contains(reverse_path_key) == false
}
to Absent
emit LinkValidated
}
transition CloseOpenCompleted {
on input CloseCompleted { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
guard "completion_policy_satisfied" {
completion_policy_is_satisfied(
self.completion_policy,
self.completion_supervisor_owner_key,
self.completion_reviewer_quorum_threshold,
self.host_confirmation_count,
self.principal_confirmation_count,
self.supervisor_confirmation_owner_keys,
self.reviewer_confirmation_owner_keys
)
}
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Completed
emit Closed { terminal_state: WorkLifecycleState::Completed, at_utc_ms: at_utc_ms }
}
transition CloseInProgressCompleted {
on input CloseCompleted { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
guard "completion_policy_satisfied" {
completion_policy_is_satisfied(
self.completion_policy,
self.completion_supervisor_owner_key,
self.completion_reviewer_quorum_threshold,
self.host_confirmation_count,
self.principal_confirmation_count,
self.supervisor_confirmation_owner_keys,
self.reviewer_confirmation_owner_keys
)
}
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Completed
emit Closed { terminal_state: WorkLifecycleState::Completed, at_utc_ms: at_utc_ms }
}
transition CloseBlockedCompleted {
on input CloseCompleted { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
guard "completion_policy_satisfied" {
completion_policy_is_satisfied(
self.completion_policy,
self.completion_supervisor_owner_key,
self.completion_reviewer_quorum_threshold,
self.host_confirmation_count,
self.principal_confirmation_count,
self.supervisor_confirmation_owner_keys,
self.reviewer_confirmation_owner_keys
)
}
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Completed
emit Closed { terminal_state: WorkLifecycleState::Completed, at_utc_ms: at_utc_ms }
}
transition CloseOpenCancelled {
on input CloseCancelled { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Cancelled
emit Closed { terminal_state: WorkLifecycleState::Cancelled, at_utc_ms: at_utc_ms }
}
transition CloseInProgressCancelled {
on input CloseCancelled { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Cancelled
emit Closed { terminal_state: WorkLifecycleState::Cancelled, at_utc_ms: at_utc_ms }
}
transition CloseBlockedCancelled {
on input CloseCancelled { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Cancelled
emit Closed { terminal_state: WorkLifecycleState::Cancelled, at_utc_ms: at_utc_ms }
}
transition CloseOpenFailed {
on input CloseFailed { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Failed
emit Closed { terminal_state: WorkLifecycleState::Failed, at_utc_ms: at_utc_ms }
}
transition CloseInProgressFailed {
on input CloseFailed { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Failed
emit Closed { terminal_state: WorkLifecycleState::Failed, at_utc_ms: at_utc_ms }
}
transition CloseBlockedFailed {
on input CloseFailed { expected_revision, at_utc_ms }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
update {
self.revision += 1;
self.claim_owner_key = None;
self.claimed_at_utc_ms = None;
self.lease_expires_at_utc_ms = None;
self.terminal_at_utc_ms = Some(at_utc_ms);
}
to Failed
emit Closed { terminal_state: WorkLifecycleState::Failed, at_utc_ms: at_utc_ms }
}
transition AddEvidenceOpen {
on input AddEvidence { expected_revision, evidence_kind, confirming_owner_key }
guard { self.lifecycle_phase == Phase::Open && self.revision == expected_revision }
guard "owner_key_present_for_kind" {
evidence_kind_owner_key_present(evidence_kind, confirming_owner_key)
}
update {
self.revision += 1;
self.evidence_count += 1;
if evidence_kind == WorkEvidenceKind::HostConfirmation {
self.host_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::PrincipalConfirmation {
self.principal_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
self.supervisor_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
self.reviewer_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
}
to Open
emit EvidenceAdded
}
transition AddEvidenceInProgress {
on input AddEvidence { expected_revision, evidence_kind, confirming_owner_key }
guard { self.lifecycle_phase == Phase::InProgress && self.revision == expected_revision }
guard "owner_key_present_for_kind" {
evidence_kind_owner_key_present(evidence_kind, confirming_owner_key)
}
update {
self.revision += 1;
self.evidence_count += 1;
if evidence_kind == WorkEvidenceKind::HostConfirmation {
self.host_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::PrincipalConfirmation {
self.principal_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
self.supervisor_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
self.reviewer_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
}
to InProgress
emit EvidenceAdded
}
transition AddEvidenceBlocked {
on input AddEvidence { expected_revision, evidence_kind, confirming_owner_key }
guard { self.lifecycle_phase == Phase::Blocked && self.revision == expected_revision }
guard "owner_key_present_for_kind" {
evidence_kind_owner_key_present(evidence_kind, confirming_owner_key)
}
update {
self.revision += 1;
self.evidence_count += 1;
if evidence_kind == WorkEvidenceKind::HostConfirmation {
self.host_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::PrincipalConfirmation {
self.principal_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
self.supervisor_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
self.reviewer_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
}
to Blocked
emit EvidenceAdded
}
transition AddEvidenceCompleted {
on input AddEvidence { expected_revision, evidence_kind, confirming_owner_key }
guard { self.lifecycle_phase == Phase::Completed && self.revision == expected_revision }
guard "owner_key_present_for_kind" {
evidence_kind_owner_key_present(evidence_kind, confirming_owner_key)
}
update {
self.revision += 1;
self.evidence_count += 1;
if evidence_kind == WorkEvidenceKind::HostConfirmation {
self.host_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::PrincipalConfirmation {
self.principal_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
self.supervisor_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
self.reviewer_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
}
to Completed
emit EvidenceAdded
}
transition AddEvidenceCancelled {
on input AddEvidence { expected_revision, evidence_kind, confirming_owner_key }
guard { self.lifecycle_phase == Phase::Cancelled && self.revision == expected_revision }
guard "owner_key_present_for_kind" {
evidence_kind_owner_key_present(evidence_kind, confirming_owner_key)
}
update {
self.revision += 1;
self.evidence_count += 1;
if evidence_kind == WorkEvidenceKind::HostConfirmation {
self.host_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::PrincipalConfirmation {
self.principal_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
self.supervisor_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
self.reviewer_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
}
to Cancelled
emit EvidenceAdded
}
transition AddEvidenceFailed {
on input AddEvidence { expected_revision, evidence_kind, confirming_owner_key }
guard { self.lifecycle_phase == Phase::Failed && self.revision == expected_revision }
guard "owner_key_present_for_kind" {
evidence_kind_owner_key_present(evidence_kind, confirming_owner_key)
}
update {
self.revision += 1;
self.evidence_count += 1;
if evidence_kind == WorkEvidenceKind::HostConfirmation {
self.host_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::PrincipalConfirmation {
self.principal_confirmation_count += 1;
}
if evidence_kind == WorkEvidenceKind::SupervisorConfirmation {
self.supervisor_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
if evidence_kind == WorkEvidenceKind::ReviewerConfirmation {
self.reviewer_confirmation_owner_keys.insert(confirming_owner_key.get("value"));
}
}
to Failed
emit EvidenceAdded
}
transition ClassifyPublicErrorNotFound {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyWorkGraphPublicError { kind }
guard "not_found_class" {
kind == WorkGraphErrorKind::NotFound
|| kind == WorkGraphErrorKind::AttentionNotFound
}
update {}
to Absent
emit WorkGraphPublicErrorClassified {
kind: kind,
public_class: WorkGraphPublicErrorClass::NotFound
}
}
transition ClassifyPublicErrorConflict {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyWorkGraphPublicError { kind }
guard "conflict_class" {
kind == WorkGraphErrorKind::StaleRevision
|| kind == WorkGraphErrorKind::Conflict
}
update {}
to Absent
emit WorkGraphPublicErrorClassified {
kind: kind,
public_class: WorkGraphPublicErrorClass::Conflict
}
}
transition ClassifyPublicErrorInvalidTransition {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyWorkGraphPublicError { kind }
guard "invalid_transition_class" {
kind == WorkGraphErrorKind::InvalidTransition
}
update {}
to Absent
emit WorkGraphPublicErrorClassified {
kind: kind,
public_class: WorkGraphPublicErrorClass::InvalidTransition
}
}
transition ClassifyPublicErrorInvalidArguments {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyWorkGraphPublicError { kind }
guard "invalid_arguments_class" {
kind == WorkGraphErrorKind::InvalidInput
|| kind == WorkGraphErrorKind::InvalidTimestampMillis
}
update {}
to Absent
emit WorkGraphPublicErrorClassified {
kind: kind,
public_class: WorkGraphPublicErrorClass::InvalidArguments
}
}
transition ClassifyPublicErrorCapabilityUnavailable {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyWorkGraphPublicError { kind }
guard "capability_unavailable_class" {
kind == WorkGraphErrorKind::UnsupportedBackend
}
update {}
to Absent
emit WorkGraphPublicErrorClassified {
kind: kind,
public_class: WorkGraphPublicErrorClass::CapabilityUnavailable
}
}
transition ClassifyPublicErrorStoreError {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyWorkGraphPublicError { kind }
guard "store_error_class" {
kind == WorkGraphErrorKind::Store
}
update {}
to Absent
emit WorkGraphPublicErrorClassified {
kind: kind,
public_class: WorkGraphPublicErrorClass::StoreError
}
}
transition ClassifyTerminalityTerminal {
per_phase [Completed, Cancelled, Failed]
on input ClassifyTerminality {}
update {}
to Absent
emit WorkItemTerminalityClassified { terminal: true }
}
transition ClassifyTerminalityLive {
per_phase [Absent, Open, InProgress, Blocked]
on input ClassifyTerminality {}
update {}
to Absent
emit WorkItemTerminalityClassified { terminal: false }
}
helper claim_time_window_eligible(
due_at_utc_ms: Option<u64>,
not_before_utc_ms: Option<u64>,
snoozed_until_utc_ms: Option<u64>,
now_utc_ms: u64
) -> bool {
(if due_at_utc_ms == None { true } else { due_at_utc_ms.get("value") <= now_utc_ms })
&& (if not_before_utc_ms == None { true } else { not_before_utc_ms.get("value") <= now_utc_ms })
&& (if snoozed_until_utc_ms == None { true } else { snoozed_until_utc_ms.get("value") <= now_utc_ms })
}
transition ClassifyReadinessOpen {
per_phase [Open]
on input ClassifyReadiness { now_utc_ms }
update {}
to Open
emit WorkItemReadinessClassified {
ready: self.unresolved_blocker_count == 0
&& claim_time_window_eligible(
self.due_at_utc_ms,
self.not_before_utc_ms,
self.snoozed_until_utc_ms,
now_utc_ms
)
}
}
transition ClassifyReadinessInProgress {
per_phase [InProgress]
on input ClassifyReadiness { now_utc_ms }
update {}
to InProgress
emit WorkItemReadinessClassified {
ready: self.claim_owner_key != None
&& self.lease_expires_at_utc_ms != None
&& self.lease_expires_at_utc_ms.get("value") <= now_utc_ms
&& self.unresolved_blocker_count == 0
&& claim_time_window_eligible(
self.due_at_utc_ms,
self.not_before_utc_ms,
self.snoozed_until_utc_ms,
now_utc_ms
)
}
}
transition ClassifyReadinessNotClaimable {
per_phase [Absent, Blocked, Completed, Cancelled, Failed]
on input ClassifyReadiness { now_utc_ms }
update {}
to Absent
emit WorkItemReadinessClassified { ready: false }
}
transition ClassifyBlockerSatisfaction {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyBlockerSatisfied { blocker_present, blocker_lifecycle_phase }
update {}
to Absent
emit BlockerSatisfactionClassified {
satisfied: blocker_present && blocker_lifecycle_phase == WorkLifecycleState::Completed
}
}
transition ClassifyCreateStatusAdmissionOpen {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_open" { requested_status == WorkLifecycleState::Open }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::AdmittedOpen }
}
transition ClassifyCreateStatusAdmissionBlocked {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_blocked" { requested_status == WorkLifecycleState::Blocked }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::AdmittedBlocked }
}
transition ClassifyCreateStatusAdmissionDeniedAbsent {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_absent" { requested_status == WorkLifecycleState::Absent }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::Denied }
}
transition ClassifyCreateStatusAdmissionDeniedInProgress {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_in_progress" { requested_status == WorkLifecycleState::InProgress }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::Denied }
}
transition ClassifyCreateStatusAdmissionDeniedCompleted {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_completed" { requested_status == WorkLifecycleState::Completed }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::Denied }
}
transition ClassifyCreateStatusAdmissionDeniedCancelled {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_cancelled" { requested_status == WorkLifecycleState::Cancelled }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::Denied }
}
transition ClassifyCreateStatusAdmissionDeniedFailed {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateStatusAdmission { requested_status }
guard "requested_failed" { requested_status == WorkLifecycleState::Failed }
update {}
to Absent
emit CreateStatusAdmissionClassified { admission: WorkCreateStatusAdmissionKind::Denied }
}
transition ClassifyCreateCompletionPolicyAdmissionSelfAttest {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateCompletionPolicyAdmission { completion_policy }
guard "self_attest_admissible_at_create" { completion_policy == WorkCompletionPolicy::SelfAttest }
update {}
to Absent
emit CreateCompletionPolicyAdmissionClassified { admission: WorkCreateCompletionPolicyAdmissionKind::Admitted }
}
transition ClassifyCreateCompletionPolicyAdmissionHostConfirmed {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateCompletionPolicyAdmission { completion_policy }
guard "host_confirmed_denied_at_create" { completion_policy == WorkCompletionPolicy::HostConfirmed }
update {}
to Absent
emit CreateCompletionPolicyAdmissionClassified { admission: WorkCreateCompletionPolicyAdmissionKind::DeniedNonSelfAttest }
}
transition ClassifyCreateCompletionPolicyAdmissionPrincipalConfirmed {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateCompletionPolicyAdmission { completion_policy }
guard "principal_confirmed_denied_at_create" { completion_policy == WorkCompletionPolicy::PrincipalConfirmed }
update {}
to Absent
emit CreateCompletionPolicyAdmissionClassified { admission: WorkCreateCompletionPolicyAdmissionKind::DeniedNonSelfAttest }
}
transition ClassifyCreateCompletionPolicyAdmissionSupervisor {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateCompletionPolicyAdmission { completion_policy }
guard "supervisor_denied_at_create" { completion_policy == WorkCompletionPolicy::Supervisor }
update {}
to Absent
emit CreateCompletionPolicyAdmissionClassified { admission: WorkCreateCompletionPolicyAdmissionKind::DeniedNonSelfAttest }
}
transition ClassifyCreateCompletionPolicyAdmissionReviewerQuorum {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCreateCompletionPolicyAdmission { completion_policy }
guard "reviewer_quorum_denied_at_create" { completion_policy == WorkCompletionPolicy::ReviewerQuorum }
update {}
to Absent
emit CreateCompletionPolicyAdmissionClassified { admission: WorkCreateCompletionPolicyAdmissionKind::DeniedNonSelfAttest }
}
transition ClassifyCloseStatusAdmissionCompleted {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_completed" { requested_status == WorkLifecycleState::Completed }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::AdmittedCompleted }
}
transition ClassifyCloseStatusAdmissionCancelled {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_cancelled" { requested_status == WorkLifecycleState::Cancelled }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::AdmittedCancelled }
}
transition ClassifyCloseStatusAdmissionFailed {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_failed" { requested_status == WorkLifecycleState::Failed }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::AdmittedFailed }
}
transition ClassifyCloseStatusAdmissionDeniedAbsent {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_absent" { requested_status == WorkLifecycleState::Absent }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::DeniedNonTerminal }
}
transition ClassifyCloseStatusAdmissionDeniedOpen {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_open" { requested_status == WorkLifecycleState::Open }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::DeniedNonTerminal }
}
transition ClassifyCloseStatusAdmissionDeniedInProgress {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_in_progress" { requested_status == WorkLifecycleState::InProgress }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::DeniedNonTerminal }
}
transition ClassifyCloseStatusAdmissionDeniedBlocked {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCloseStatusAdmission { requested_status }
guard "requested_blocked" { requested_status == WorkLifecycleState::Blocked }
update {}
to Absent
emit CloseStatusAdmissionClassified { admission: WorkCloseStatusAdmissionKind::DeniedNonTerminal }
}
transition ClassifyPublicConfirmationAdmissionSelfAttest {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyPublicConfirmationAdmission { completion_policy }
guard "self_attest_public_confirmable" { completion_policy == WorkCompletionPolicy::SelfAttest }
update {}
to Absent
emit PublicConfirmationAdmissionClassified { admission: WorkPublicConfirmationAdmissionKind::Admitted }
}
transition ClassifyPublicConfirmationAdmissionHostConfirmed {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyPublicConfirmationAdmission { completion_policy }
guard "host_confirmed_requires_trusted_host" { completion_policy == WorkCompletionPolicy::HostConfirmed }
update {}
to Absent
emit PublicConfirmationAdmissionClassified { admission: WorkPublicConfirmationAdmissionKind::DeniedRequiresTrustedHost }
}
transition ClassifyPublicConfirmationAdmissionPrincipalConfirmed {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyPublicConfirmationAdmission { completion_policy }
guard "principal_confirmed_requires_trusted_host" { completion_policy == WorkCompletionPolicy::PrincipalConfirmed }
update {}
to Absent
emit PublicConfirmationAdmissionClassified { admission: WorkPublicConfirmationAdmissionKind::DeniedRequiresTrustedHost }
}
transition ClassifyPublicConfirmationAdmissionSupervisor {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyPublicConfirmationAdmission { completion_policy }
guard "supervisor_requires_trusted_host" { completion_policy == WorkCompletionPolicy::Supervisor }
update {}
to Absent
emit PublicConfirmationAdmissionClassified { admission: WorkPublicConfirmationAdmissionKind::DeniedRequiresTrustedHost }
}
transition ClassifyPublicConfirmationAdmissionReviewerQuorum {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyPublicConfirmationAdmission { completion_policy }
guard "reviewer_quorum_requires_trusted_host" { completion_policy == WorkCompletionPolicy::ReviewerQuorum }
update {}
to Absent
emit PublicConfirmationAdmissionClassified { admission: WorkPublicConfirmationAdmissionKind::DeniedRequiresTrustedHost }
}
transition ClassifyCompletionPolicyMutationAdmissionUnchanged {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCompletionPolicyMutationAdmission {
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
}
guard "completion_policy_unchanged" {
requested_completion_policy == self.completion_policy
&& requested_completion_supervisor_owner_key == self.completion_supervisor_owner_key
&& requested_completion_reviewer_quorum_threshold == self.completion_reviewer_quorum_threshold
}
update {}
to Absent
emit CompletionPolicyMutationAdmissionClassified { admission: WorkCompletionPolicyMutationAdmissionKind::Admitted }
}
transition ClassifyCompletionPolicyMutationAdmissionChanged {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyCompletionPolicyMutationAdmission {
requested_completion_policy,
requested_completion_supervisor_owner_key,
requested_completion_reviewer_quorum_threshold
}
guard "completion_policy_changed" {
requested_completion_policy != self.completion_policy
|| requested_completion_supervisor_owner_key != self.completion_supervisor_owner_key
|| requested_completion_reviewer_quorum_threshold != self.completion_reviewer_quorum_threshold
}
update {}
to Absent
emit CompletionPolicyMutationAdmissionClassified { admission: WorkCompletionPolicyMutationAdmissionKind::Denied }
}
transition ClassifyConfirmationAdmissionPrincipalRequired {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyConfirmationAdmission {
completion_policy,
completion_supervisor_owner_key,
requested_principal_owner_key,
requested_principal_kind,
supplied_evidence_kind
}
guard "principal_required" {
confirmation_denies_principal_required(completion_policy, requested_principal_owner_key)
}
update {}
to Absent
emit ConfirmationAdmissionClassified { admission: WorkConfirmationAdmissionKind::DeniedPrincipalRequired }
}
transition ClassifyConfirmationAdmissionPrincipalKindMismatch {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyConfirmationAdmission {
completion_policy,
completion_supervisor_owner_key,
requested_principal_owner_key,
requested_principal_kind,
supplied_evidence_kind
}
guard "principal_kind_mismatch" {
confirmation_denies_principal_kind_mismatch(completion_policy, requested_principal_owner_key, requested_principal_kind)
}
update {}
to Absent
emit ConfirmationAdmissionClassified { admission: WorkConfirmationAdmissionKind::DeniedPrincipalKindMismatch }
}
transition ClassifyConfirmationAdmissionSupervisorMismatch {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyConfirmationAdmission {
completion_policy,
completion_supervisor_owner_key,
requested_principal_owner_key,
requested_principal_kind,
supplied_evidence_kind
}
guard "supervisor_mismatch" {
confirmation_denies_supervisor_mismatch(completion_policy, completion_supervisor_owner_key, requested_principal_owner_key)
}
update {}
to Absent
emit ConfirmationAdmissionClassified { admission: WorkConfirmationAdmissionKind::DeniedSupervisorMismatch }
}
transition ClassifyConfirmationAdmissionSelfAttestEmpty {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyConfirmationAdmission {
completion_policy,
completion_supervisor_owner_key,
requested_principal_owner_key,
requested_principal_kind,
supplied_evidence_kind
}
guard "self_attest_empty" {
confirmation_denies_self_attest_empty(completion_policy, supplied_evidence_kind)
}
update {}
to Absent
emit ConfirmationAdmissionClassified { admission: WorkConfirmationAdmissionKind::DeniedSelfAttestEmptyEvidenceKind }
}
transition ClassifyConfirmationAdmissionEvidenceKind {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyConfirmationAdmission {
completion_policy,
completion_supervisor_owner_key,
requested_principal_owner_key,
requested_principal_kind,
supplied_evidence_kind
}
guard "evidence_kind_mismatch" {
confirmation_denies_evidence_kind(completion_policy, completion_supervisor_owner_key, requested_principal_owner_key, requested_principal_kind, supplied_evidence_kind)
}
update {}
to Absent
emit ConfirmationAdmissionClassified { admission: WorkConfirmationAdmissionKind::DeniedEvidenceKind }
}
transition ClassifyConfirmationAdmissionAdmitted {
per_phase [Absent, Open, InProgress, Blocked, Completed, Cancelled, Failed]
on input ClassifyConfirmationAdmission {
completion_policy,
completion_supervisor_owner_key,
requested_principal_owner_key,
requested_principal_kind,
supplied_evidence_kind
}
guard "confirmation_admissible" {
confirmation_admits(completion_policy, completion_supervisor_owner_key, requested_principal_owner_key, requested_principal_kind, supplied_evidence_kind)
}
update {}
to Absent
emit ConfirmationAdmissionClassified { admission: WorkConfirmationAdmissionKind::Admitted }
}
}
}
impl serde::Serialize for WorkLifecycleState {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(match self {
Self::Absent => "absent",
Self::Open => "open",
Self::InProgress => "in_progress",
Self::Blocked => "blocked",
Self::Completed => "completed",
Self::Cancelled => "cancelled",
Self::Failed => "failed",
})
}
}
impl<'de> serde::Deserialize<'de> for WorkLifecycleState {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <String as serde::Deserialize>::deserialize(deserializer)?;
match value.as_str() {
"absent" | "Absent" => Ok(Self::Absent),
"open" | "Open" => Ok(Self::Open),
"in_progress" | "InProgress" => Ok(Self::InProgress),
"blocked" | "Blocked" => Ok(Self::Blocked),
"completed" | "Completed" => Ok(Self::Completed),
"cancelled" | "Cancelled" => Ok(Self::Cancelled),
"failed" | "Failed" => Ok(Self::Failed),
other => Err(serde::de::Error::custom(format!(
"invalid WorkLifecycleState `{other}`"
))),
}
}
}
#[derive(serde::Serialize, serde::Deserialize)]
struct WorkGraphLifecycleMachineStateWire {
lifecycle_phase: WorkLifecycleState,
revision: u64,
unresolved_blocker_count: u64,
#[serde(default)]
topology_item_keys: std::collections::BTreeSet<WorkItemKey>,
#[serde(default)]
topology_edge_keys: std::collections::BTreeSet<WorkEdgeKey>,
#[serde(default)]
blocks_reachability: std::collections::BTreeSet<WorkDependencyPathKey>,
#[serde(default)]
parent_reachability: std::collections::BTreeSet<WorkDependencyPathKey>,
claim_owner_key: Option<WorkOwnerKey>,
claimed_at_utc_ms: Option<u64>,
lease_expires_at_utc_ms: Option<u64>,
due_at_utc_ms: Option<u64>,
not_before_utc_ms: Option<u64>,
snoozed_until_utc_ms: Option<u64>,
#[serde(default)]
completion_policy: WorkCompletionPolicy,
#[serde(default)]
completion_supervisor_owner_key: Option<WorkOwnerKey>,
#[serde(default)]
completion_reviewer_quorum_threshold: Option<u64>,
terminal_at_utc_ms: Option<u64>,
evidence_count: u64,
#[serde(default)]
host_confirmation_count: u64,
#[serde(default)]
principal_confirmation_count: u64,
#[serde(default)]
supervisor_confirmation_owner_keys: std::collections::BTreeSet<WorkOwnerKey>,
#[serde(default)]
reviewer_confirmation_owner_keys: std::collections::BTreeSet<WorkOwnerKey>,
}
impl From<&WorkGraphLifecycleMachineState> for WorkGraphLifecycleMachineStateWire {
fn from(state: &WorkGraphLifecycleMachineState) -> Self {
Self {
lifecycle_phase: state.lifecycle_phase,
revision: state.revision,
unresolved_blocker_count: state.unresolved_blocker_count,
topology_item_keys: state.topology_item_keys.clone(),
topology_edge_keys: state.topology_edge_keys.clone(),
blocks_reachability: state.blocks_reachability.clone(),
parent_reachability: state.parent_reachability.clone(),
claim_owner_key: state.claim_owner_key.clone(),
claimed_at_utc_ms: state.claimed_at_utc_ms,
lease_expires_at_utc_ms: state.lease_expires_at_utc_ms,
due_at_utc_ms: state.due_at_utc_ms,
not_before_utc_ms: state.not_before_utc_ms,
snoozed_until_utc_ms: state.snoozed_until_utc_ms,
completion_policy: state.completion_policy,
completion_supervisor_owner_key: state.completion_supervisor_owner_key.clone(),
completion_reviewer_quorum_threshold: state.completion_reviewer_quorum_threshold,
terminal_at_utc_ms: state.terminal_at_utc_ms,
evidence_count: state.evidence_count,
host_confirmation_count: state.host_confirmation_count,
principal_confirmation_count: state.principal_confirmation_count,
supervisor_confirmation_owner_keys: state.supervisor_confirmation_owner_keys.clone(),
reviewer_confirmation_owner_keys: state.reviewer_confirmation_owner_keys.clone(),
}
}
}
impl From<WorkGraphLifecycleMachineStateWire> for WorkGraphLifecycleMachineState {
fn from(wire: WorkGraphLifecycleMachineStateWire) -> Self {
Self {
lifecycle_phase: wire.lifecycle_phase,
revision: wire.revision,
unresolved_blocker_count: wire.unresolved_blocker_count,
topology_item_keys: wire.topology_item_keys,
topology_edge_keys: wire.topology_edge_keys,
blocks_reachability: wire.blocks_reachability,
parent_reachability: wire.parent_reachability,
claim_owner_key: wire.claim_owner_key,
claimed_at_utc_ms: wire.claimed_at_utc_ms,
lease_expires_at_utc_ms: wire.lease_expires_at_utc_ms,
due_at_utc_ms: wire.due_at_utc_ms,
not_before_utc_ms: wire.not_before_utc_ms,
snoozed_until_utc_ms: wire.snoozed_until_utc_ms,
completion_policy: wire.completion_policy,
completion_supervisor_owner_key: wire.completion_supervisor_owner_key,
completion_reviewer_quorum_threshold: wire.completion_reviewer_quorum_threshold,
terminal_at_utc_ms: wire.terminal_at_utc_ms,
evidence_count: wire.evidence_count,
host_confirmation_count: wire.host_confirmation_count,
principal_confirmation_count: wire.principal_confirmation_count,
supervisor_confirmation_owner_keys: wire.supervisor_confirmation_owner_keys,
reviewer_confirmation_owner_keys: wire.reviewer_confirmation_owner_keys,
}
}
}
impl serde::Serialize for WorkGraphLifecycleMachineState {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
WorkGraphLifecycleMachineStateWire::from(self).serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for WorkGraphLifecycleMachineState {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
WorkGraphLifecycleMachineStateWire::deserialize(deserializer).map(Self::from)
}
}