Skip to main content

everruns_core/
workspace.rs

1// Workspace domain types
2//
3// Design intent lives in `specs/workspace.md`.
4//
5// A Workspace is an org-scoped, named working area that holds the files an
6// agent reads and writes during execution. Sessions attach to a Workspace;
7// multiple sessions may share one Workspace, or each session may own its
8// own (the default, per session creation).
9
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12use uuid::Uuid;
13
14use crate::typed_id::WorkspaceId;
15
16#[cfg(feature = "openapi")]
17use utoipa::ToSchema;
18
19/// Workspace lifecycle status.
20///
21/// Mirrors the standard building-block lifecycle from `specs/models.md`:
22/// - `active`: assignable to sessions, editable, listed by default.
23/// - `archived`: hidden from default lists, not assignable to new sessions,
24///   files become read-only.
25/// - `deleted`: tombstone; detail/list APIs return 404 except for historical
26///   references (existing session links).
27#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
28#[cfg_attr(feature = "openapi", derive(ToSchema))]
29#[serde(rename_all = "lowercase")]
30pub enum WorkspaceStatus {
31    Active,
32    Archived,
33    Deleted,
34}
35
36impl std::fmt::Display for WorkspaceStatus {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        match self {
39            WorkspaceStatus::Active => write!(f, "active"),
40            WorkspaceStatus::Archived => write!(f, "archived"),
41            WorkspaceStatus::Deleted => write!(f, "deleted"),
42        }
43    }
44}
45
46impl From<&str> for WorkspaceStatus {
47    fn from(s: &str) -> Self {
48        match s {
49            "archived" => WorkspaceStatus::Archived,
50            "deleted" => WorkspaceStatus::Deleted,
51            _ => WorkspaceStatus::Active,
52        }
53    }
54}
55
56/// A Workspace — org-scoped named working area.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[cfg_attr(feature = "openapi", derive(ToSchema))]
59pub struct Workspace {
60    /// External identifier (`wsp_<32-hex>`). Shown as `id` in API responses.
61    #[serde(rename = "id")]
62    #[cfg_attr(
63        feature = "openapi",
64        schema(value_type = String, example = "wsp_01933b5a000070008000000000000001")
65    )]
66    pub public_id: WorkspaceId,
67    /// Internal UUID primary key. Used for FK references. Never exposed in API.
68    #[serde(skip, default = "Uuid::nil")]
69    pub internal_id: Uuid,
70    /// Human-readable name, unique per org while not deleted.
71    pub name: String,
72    /// Optional human-readable description.
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub description: Option<String>,
75    /// Principal that created the workspace (free-form; resolved at the domain layer).
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub owner_principal_id: Option<String>,
78    /// Resolved owner user, when known.
79    #[serde(default, skip_serializing_if = "Option::is_none")]
80    pub resolved_owner_user_id: Option<Uuid>,
81    /// Lifecycle status.
82    pub status: WorkspaceStatus,
83    pub created_at: DateTime<Utc>,
84    pub updated_at: DateTime<Utc>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub archived_at: Option<DateTime<Utc>>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub deleted_at: Option<DateTime<Utc>>,
89}