Skip to main content

qualia_client_core/wellfair/
host_state.rs

1//! Host operating-state DTOs consumed by the WellFair shell (Workstream 2).
2//!
3//! UI renders these snapshots; it does not derive policy or vault authority.
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum VaultLifecycle {
10    Unconfigured,
11    Locked,
12    Unlocked,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "snake_case")]
17pub enum NetworkExposure {
18    Offline,
19    LocalOnly,
20    ExternalCapable,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum SyncQueueState {
26    Idle,
27    Queued,
28    Sending,
29    Acknowledged,
30    Conflicted,
31    Rejected,
32    Revoked,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum SensitivityClassDto {
38    Public,
39    Restricted,
40    Classified,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44pub struct AccessibilityPreferences {
45    pub high_contrast: bool,
46    pub reduced_motion: bool,
47    pub text_scale_percent: u8,
48    pub screen_reader_hints: bool,
49}
50
51impl Default for AccessibilityPreferences {
52    fn default() -> Self {
53        Self {
54            high_contrast: false,
55            reduced_motion: false,
56            text_scale_percent: 100,
57            screen_reader_hints: true,
58        }
59    }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63pub struct ProvenanceHop {
64    pub label: String,
65    pub evidence_type: String,
66    pub hash_prefix: String,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70pub struct ConsentGrantDraft {
71    pub recipient: String,
72    pub purpose: String,
73    pub fields: Vec<String>,
74    pub expires_at_unix: Option<u64>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78#[serde(rename_all = "snake_case")]
79pub enum PolicyDecisionDto {
80    Permit {
81        obligations: Vec<String>,
82    },
83    Deny {
84        reasons: Vec<String>,
85    },
86    Prompt {
87        requested_consent: ConsentGrantDraft,
88    },
89    Suspend {
90        required_approvals: u8,
91    },
92}
93
94/// Outcome of a policy-gated write that may enter the guardianship escrow.
95///
96/// `Committed` — the record was written (quins materialized). `Suspended` — a proxy write of a
97/// protected record is held pending M-of-N guardian co-signature; the returned `proposal_id`
98/// identifies the pending [`crate::wellfair`] proposal in the approval tray.
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(rename_all = "snake_case", tag = "outcome")]
101pub enum SubmitOutcome {
102    Committed { quins: usize },
103    Suspended { proposal_id: String, threshold: u8 },
104}
105
106/// UI view of a guardianship proposal + its derived approval status.
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct GuardianshipProposalView {
109    pub proposal_id: String,
110    pub principal_did: String,
111    pub proxy_did: String,
112    pub escrowed_kind: String,
113    pub reason: String,
114    pub created_unix: u32,
115    /// "pending" | "ratified" | "denied".
116    pub state: String,
117    pub approvals: u8,
118    pub threshold: u8,
119    pub denied_by: Option<String>,
120    pub denial_reason: Option<String>,
121    /// Whether the escrowed record has been committed (true once ratified + written).
122    pub committed: bool,
123}
124
125impl GuardianshipProposalView {
126    pub fn from_status(
127        proposal: &wellfare_core::guardianship::GuardianshipProposal,
128        status: &wellfare_core::guardianship::ProposalStatus,
129        committed: bool,
130    ) -> Self {
131        use wellfare_core::guardianship::ProposalState;
132        let state = match status.state {
133            ProposalState::Pending => "pending",
134            ProposalState::Ratified => "ratified",
135            ProposalState::Denied => "denied",
136        }
137        .to_string();
138        Self {
139            proposal_id: proposal.id.clone(),
140            principal_did: proposal.principal_did.clone(),
141            proxy_did: proposal.proxy_did.clone(),
142            escrowed_kind: proposal.escrowed_kind.clone(),
143            reason: proposal.reason.clone(),
144            created_unix: proposal.created_unix,
145            state,
146            approvals: status.approvals,
147            threshold: status.threshold,
148            denied_by: status.denied_by.clone(),
149            denial_reason: status.denial_reason.clone(),
150            committed,
151        }
152    }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156pub struct WellfairHostSnapshot {
157    pub vault: VaultLifecycle,
158    pub network: NetworkExposure,
159    pub sync_state: SyncQueueState,
160    pub demo_mode: bool,
161    pub owner_label: String,
162    pub accessibility: AccessibilityPreferences,
163    pub pending_jobs: u32,
164    pub health_record_count: u32,
165    pub graph_quin_count: u32,
166    pub last_checkpoint_prefix: Option<String>,
167    pub capabilities_ready: bool,
168    pub host_api_version: String,
169}
170
171impl Default for WellfairHostSnapshot {
172    fn default() -> Self {
173        Self {
174            vault: VaultLifecycle::Unconfigured,
175            network: NetworkExposure::Offline,
176            sync_state: SyncQueueState::Idle,
177            demo_mode: false,
178            owner_label: String::new(),
179            accessibility: AccessibilityPreferences::default(),
180            pending_jobs: 0,
181            health_record_count: 0,
182            graph_quin_count: 0,
183            last_checkpoint_prefix: None,
184            capabilities_ready: false,
185            host_api_version: crate::qapp_install::SUPPORTED_HOST_API_VERSION.to_string(),
186        }
187    }
188}
189
190/// Phase 0 fixture snapshot until VaultService and IdentityService wire live state.
191pub fn fixture_host_snapshot() -> WellfairHostSnapshot {
192    WellfairHostSnapshot {
193        vault: VaultLifecycle::Locked,
194        network: NetworkExposure::LocalOnly,
195        sync_state: SyncQueueState::Idle,
196        demo_mode: false,
197        owner_label: "Owner vault (fixture)".to_string(),
198        accessibility: AccessibilityPreferences::default(),
199        pending_jobs: 0,
200        health_record_count: 0,
201        graph_quin_count: 0,
202        last_checkpoint_prefix: None,
203        capabilities_ready: true,
204        host_api_version: crate::qapp_install::SUPPORTED_HOST_API_VERSION.to_string(),
205    }
206}
207
208pub fn demo_host_snapshot() -> WellfairHostSnapshot {
209    WellfairHostSnapshot {
210        vault: VaultLifecycle::Unlocked,
211        network: NetworkExposure::Offline,
212        sync_state: SyncQueueState::Idle,
213        demo_mode: true,
214        owner_label: "Demo persona (isolated)".to_string(),
215        ..WellfairHostSnapshot::default()
216    }
217}