use serde::{Deserialize, Serialize};
use crate::vitals::CognitiveVitals;
use crate::{ContextFreshness, ImpactDecision};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TrajectoryStep {
pub step_id: String,
pub timestamp_ms: i64,
pub adapter: String,
pub operation: String,
#[serde(default)]
pub inputs_preview: Option<String>,
#[serde(default)]
pub outputs_preview: Option<String>,
pub duration_ms: u64,
pub success: bool,
#[serde(default)]
pub error: Option<String>,
#[serde(default)]
pub vitals: Option<CognitiveVitals>,
#[serde(default)]
pub freshness_at_step: Option<ContextFreshness>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frame_vars: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_telemetry: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TrajectoryOutcome {
Success,
PartialSuccess,
Failure,
Aborted,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "kind", content = "details")]
pub enum FailureKind {
AdapterTypo {
offered: String,
#[serde(default)]
suggestion: Option<String>,
},
ValidatorReject {
rule: String,
},
AdapterTimeout {
adapter: String,
ms: u64,
},
ToolError {
tool: String,
message: String,
},
LoopGuardFire {
tool: String,
repeat_count: u32,
},
Other {
message: String,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProposalEnvelope {
pub schema_version: u32,
pub original_hash: String,
pub proposed_hash: String,
pub kind: String,
pub rationale: String,
pub freshness_at_proposal: ContextFreshness,
pub impact_decision: ImpactDecision,
}