Skip to main content

edda_ledger/
domain.rs

1//! Domain types — public API contracts for edda-ledger consumers.
2//!
3//! These types represent domain concepts that are independent of the storage
4//! backend. They are the stable contract for downstream crates.
5//!
6//! Storage-internal types (e.g. `DecisionRow`) remain in `sqlite_store/types.rs`
7//! and are not exposed outside edda-ledger.
8
9/// The type of recurring pattern detected.
10#[derive(Debug, Clone, serde::Serialize)]
11#[serde(rename_all = "snake_case")]
12pub enum PatternType {
13    RecurringDecision,
14    ChiefRepeatedAction,
15    RollbackTrend,
16}
17
18/// A single detected pattern in a village's decision history.
19#[derive(Debug, Clone, serde::Serialize)]
20pub struct DetectedPattern {
21    pub pattern_type: PatternType,
22    pub key: String,
23    pub domain: String,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub authority: Option<String>,
26    pub occurrences: usize,
27    pub first_seen: String,
28    pub last_seen: String,
29    pub dates: Vec<String>,
30    pub description: String,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub trending_up: Option<bool>,
33}
34
35/// Result of pattern detection for a village.
36#[derive(Debug, Clone, serde::Serialize)]
37pub struct PatternDetectionResult {
38    pub village_id: String,
39    pub lookback_days: u32,
40    pub after: String,
41    pub total_patterns: usize,
42    pub patterns: Vec<DetectedPattern>,
43}
44
45/// Statistics for a village's decisions.
46#[derive(Debug, Clone, serde::Serialize)]
47pub struct VillageStats {
48    pub village_id: String,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub period: Option<VillageStatsPeriod>,
51    pub total_decisions: usize,
52    pub decisions_per_day: f64,
53    pub by_status: std::collections::HashMap<String, usize>,
54    pub by_authority: std::collections::HashMap<String, usize>,
55    pub top_domains: Vec<DomainCount>,
56    pub rollback_rate: f64,
57    pub trend: Vec<DayCount>,
58}
59
60/// Time period for village stats.
61#[derive(Debug, Clone, serde::Serialize)]
62pub struct VillageStatsPeriod {
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub after: Option<String>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub before: Option<String>,
67}
68
69/// Domain with decision count.
70#[derive(Debug, Clone, serde::Serialize)]
71pub struct DomainCount {
72    pub domain: String,
73    pub count: usize,
74}
75
76/// Daily decision count.
77#[derive(Debug, Clone, serde::Serialize)]
78pub struct DayCount {
79    pub date: String,
80    pub count: usize,
81}
82
83/// Aggregated outcome metrics for a decision.
84#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
85pub struct OutcomeMetrics {
86    pub decision_event_id: String,
87    pub decision_key: String,
88    pub decision_value: String,
89    pub decision_ts: String,
90    pub total_executions: u64,
91    pub success_count: u64,
92    pub failed_count: u64,
93    pub cancelled_count: u64,
94    pub success_rate: f64,
95    pub total_cost_usd: f64,
96    pub total_tokens_in: u64,
97    pub total_tokens_out: u64,
98    pub avg_latency_ms: f64,
99    pub first_execution_ts: Option<String>,
100    pub last_execution_ts: Option<String>,
101}
102
103/// An entry in a causal chain traversal result (domain view).
104///
105/// Unlike the internal `ChainEntry` which embeds `DecisionRow`, this type
106/// uses `DecisionView` to hide storage details from consumers.
107#[derive(Debug, Clone)]
108pub struct ChainEntryView {
109    pub decision: crate::view::DecisionView,
110    pub relation: String,
111    pub depth: usize,
112}
113
114/// An execution event linked to a decision.
115#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
116pub struct ExecutionLinked {
117    pub event_id: String,
118    pub ts: String,
119    pub status: String,
120    pub runtime: Option<String>,
121    pub model: Option<String>,
122    pub cost_usd: Option<f64>,
123    pub token_in: Option<u64>,
124    pub token_out: Option<u64>,
125    pub latency_ms: Option<u64>,
126}
127
128// ── Types moved from sqlite_store/types.rs ──────────────────────────
129
130/// A review bundle row.
131#[derive(Debug, Clone)]
132pub struct BundleRow {
133    pub event_id: String,
134    pub bundle_id: String,
135    pub status: String,
136    pub risk_level: String,
137    pub total_added: i64,
138    pub total_deleted: i64,
139    pub files_changed: i64,
140    pub tests_passed: i64,
141    pub tests_failed: i64,
142    pub suggested_action: String,
143    pub branch: String,
144    pub created_at: String,
145}
146
147/// A dependency edge between two decision keys.
148#[derive(Debug, Clone)]
149pub struct DependencyEdge {
150    pub source_key: String,
151    pub target_key: String,
152    pub dep_type: String,
153    pub created_event: Option<String>,
154    pub created_at: String,
155}
156
157/// Parameters for inserting an imported decision from another project.
158pub struct ImportParams<'a> {
159    pub event: &'a edda_core::types::Event,
160    pub key: &'a str,
161    pub value: &'a str,
162    pub reason: &'a str,
163    pub domain: &'a str,
164    pub scope: &'a str,
165    pub source_project_id: &'a str,
166    pub source_event_id: &'a str,
167    pub is_active: bool,
168}
169
170/// A task brief row.
171#[derive(Debug, Clone)]
172pub struct TaskBriefRow {
173    pub task_id: String,
174    pub intake_event_id: String,
175    pub title: String,
176    pub intent: edda_core::types::TaskBriefIntent,
177    pub source_url: String,
178    pub status: edda_core::types::TaskBriefStatus,
179    pub branch: String,
180    pub iterations: i64,
181    pub artifacts: String,
182    pub decisions: String,
183    pub last_feedback: Option<String>,
184    pub created_at: String,
185    pub updated_at: String,
186}
187
188/// A device token row.
189#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
190pub struct DeviceTokenRow {
191    pub token_hash: String,
192    pub device_name: String,
193    pub paired_at: String,
194    pub paired_from_ip: String,
195    pub revoked_at: Option<String>,
196    pub pair_event_id: String,
197    pub revoke_event_id: Option<String>,
198}
199
200/// A decide snapshot row.
201#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
202pub struct DecideSnapshotRow {
203    pub event_id: String,
204    pub context_hash: String,
205    pub engine_version: String,
206    pub schema_version: String,
207    pub redaction_level: String,
208    pub village_id: Option<String>,
209    pub cycle_id: Option<String>,
210    pub has_blobs: bool,
211    pub created_at: String,
212}
213
214/// A suggestion row.
215#[derive(Debug, Clone)]
216pub struct SuggestionRow {
217    pub id: String,
218    pub event_type: String,
219    pub source_layer: String,
220    pub source_refs: String,
221    pub summary: String,
222    pub suggested_because: String,
223    pub detail: String,
224    pub tags: String,
225    pub status: String,
226    pub created_at: String,
227    pub reviewed_at: Option<String>,
228}