Skip to main content

aion_core/
describe.rs

1//! Describe-workflow response projection.
2//!
3//! The ops console's `POST /workflows/describe` read consumes exactly this shape:
4//! a workflow [`WorkflowSummary`] projection plus the run's event [`Event`]
5//! history as plain JSON. Defining it here lets the same type be exported to
6//! TypeScript (so the generated bindings match the wire by construction) and be
7//! produced directly by the HTTP handler at the transport boundary.
8
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11
12use crate::{ActivityId, Event, WorkflowSummary};
13
14/// Response to a describe-workflow request.
15///
16/// `history` is the run's events as plain serialized [`Event`] values (never a
17/// protobuf-derived envelope), so the ops console decodes each entry directly.
18/// When `include_history` is false the server returns an empty `history`.
19///
20/// `unserved` is computed independently of `include_history`: it is the answer
21/// to "can anything still advance this run", and an operator who asked for the
22/// summary alone still needs it.
23#[derive(Serialize, Deserialize, ts_rs::TS, Clone, Debug, PartialEq)]
24pub struct DescribeWorkflowResponse {
25    /// Workflow summary projected from authoritative history, when the workflow
26    /// exists.
27    pub summary: Option<WorkflowSummary>,
28    /// The run's event history as plain serialized events.
29    pub history: Vec<Event>,
30    /// Every activity this run's history records as dispatched and unterminated
31    /// that the fleet cannot currently serve.
32    ///
33    /// EMPTY is the healthy answer, and it is the only healthy answer: an
34    /// activity a live compatible worker could take is never listed here. A
35    /// non-empty list is the difference between "a worker is working on it" and
36    /// "it is parked with nobody to take it", which the projected
37    /// [`crate::WorkflowStatus`] alone cannot express — `Running` covers both.
38    pub unserved: Vec<UnservedActivity>,
39}
40
41/// One in-flight activity whose recorded dispatch address is not being served.
42///
43/// The activity's identity, address, and dispatch instant come from the run's
44/// own recorded history; the verdict and the counts come from the live
45/// connected-worker fleet at read time. Both halves are named so an operator can
46/// see which is which.
47#[derive(Serialize, Deserialize, ts_rs::TS, Clone, Debug, PartialEq, Eq)]
48pub struct UnservedActivity {
49    /// Activity ordinal recorded in history.
50    pub activity_id: ActivityId,
51    /// Activity type the dispatch needs served.
52    pub activity_type: String,
53    /// Task queue the dispatch was recorded against.
54    pub task_queue: String,
55    /// Node affinity recorded on the dispatch, if any.
56    pub node: Option<String>,
57    /// One-based delivery attempt recorded on the `ActivityStarted`.
58    pub attempt: u32,
59    /// When the engine recorded the dispatch — NOT when a worker took it, which
60    /// is precisely the thing that never happened.
61    pub dispatched_at: DateTime<Utc>,
62    /// Canonical queue-service reason (`NO_QUEUE_DECLARATION`,
63    /// `NO_LIVE_POLLERS`, `POLLERS_INCOMPATIBLE`), in the same vocabulary the
64    /// dispatch refusals and the server logs use.
65    pub reason: String,
66    /// One sentence naming what an operator has to fix.
67    pub detail: String,
68    /// Workers connected for the `(namespace, task_queue)` pool, whatever they
69    /// serve.
70    pub workers_in_pool: u64,
71    /// Of those, workers advertising this activity type.
72    pub workers_serving_activity: u64,
73    /// Of those, workers that also satisfy the dispatch's node pin.
74    pub compatible_workers: u64,
75    /// Whether THIS server process is currently holding the dispatch in its
76    /// selection wait.
77    ///
78    /// False with a non-empty verdict is its own signal: history says the
79    /// activity is in flight, the fleet cannot serve it, and no selection in
80    /// this process is even waiting for it — the shape a run left behind by a
81    /// restart takes.
82    pub dispatch_parked: bool,
83}