Skip to main content

dpm_model/run/
view.rs

1//! What a run looks like when read: durable lifecycle beside derived observation.
2
3use super::{ActivityTally, LifecycleEvent, RunLink, RunRecord, RunState};
4use crate::{ActorId, WorkStatus};
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8/// What an observer can honestly say about a run at one clock reading.
9///
10/// The first five follow the last recorded transition. `Stale` and `Unknown` are never stored:
11/// they are derived from receipt times and attribution each time a view is computed, so a run
12/// that goes silent is never labelled finished, and one that resumes needs no repair.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum ObservedStatus {
16    /// Working, and heard from recently.
17    Working,
18    /// Waiting, and heard from recently.
19    Waiting,
20    /// Recorded as failed.
21    Failed,
22    /// Recorded as interrupted.
23    Interrupted,
24    /// Recorded as completed.
25    Completed,
26    /// Not finished according to its last transition, but nothing has been received for longer
27    /// than the staleness bound. Its real state is unknown.
28    Stale,
29    /// Cannot be judged here: it was recorded under another history than this store continues,
30    /// so whatever process it described is not known to be running in this one.
31    Unknown,
32}
33
34/// Whether a run was recorded in the history the project store now continues.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum LineageStatus {
38    /// Recorded under the store's current lineage.
39    Current,
40    /// Recorded under another lineage, such as before a restore. Its attribution is unchanged
41    /// and it is never reattached to this history.
42    Foreign,
43}
44
45/// Why a run that is not finished no longer matches the task it executes.
46///
47/// Runs never move task ownership; a mismatch is reported so someone can decide what to do.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(tag = "reason", rename_all = "snake_case")]
50pub enum Orphan {
51    /// The task is owned by someone other than the run's executor, or by no one.
52    OwnerChanged {
53        /// The task's current owner.
54        owner: Option<ActorId>,
55    },
56    /// The task is no longer being executed.
57    NotExecuting {
58        /// The task's current lifecycle.
59        status: WorkStatus,
60    },
61    /// The task is not in the plan this view was computed from.
62    WorkMissing,
63}
64
65/// Everything a store holds about one run, before any clock or plan is applied.
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67pub struct RunSnapshot {
68    /// The run as started.
69    pub record: RunRecord,
70    /// The latest lifecycle fact.
71    pub last: LifecycleEvent,
72    /// What the run's activity amounts to.
73    pub activity: ActivityTally,
74    /// Project operations explicitly linked to the run.
75    pub operations: Vec<RunLink>,
76}
77
78/// A run with its derived observation, evaluated at one clock reading.
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct RunView {
81    /// The run as started.
82    pub run: RunRecord,
83    /// Lifecycle truth: the last recorded transition.
84    pub state: RunState,
85    /// When that transition was recorded.
86    pub state_since: DateTime<Utc>,
87    /// Detail the reporter gave with it.
88    #[serde(default, skip_serializing_if = "Option::is_none")]
89    pub state_detail: Option<String>,
90    /// What can be said about the run now; see [`ObservedStatus`].
91    pub status: ObservedStatus,
92    /// The latest time DPM received anything from the run, from DPM's own clock.
93    pub last_receipt_at: DateTime<Utc>,
94    /// When a working or waiting run turns stale if nothing more arrives; the instant a view
95    /// should next be recomputed. Absent once the run is stale, unknown or finished.
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub stale_at: Option<DateTime<Utc>>,
98    /// Activity received, retained and latest.
99    pub activity: ActivityTally,
100    /// Whether the run belongs to the store's current history.
101    pub lineage: LineageStatus,
102    /// Present when the run is unfinished but its task no longer matches it.
103    #[serde(default, skip_serializing_if = "Option::is_none")]
104    pub orphan: Option<Orphan>,
105    /// Project operations explicitly linked to the run.
106    pub operations: Vec<RunLink>,
107    /// The clock reading this view was computed at.
108    pub evaluated_at: DateTime<Utc>,
109}