///|
/// A deliberately small diagnostic projection. Raw events, command payloads,
/// runner output and error messages never enter this type.
struct DiagnosticsJson {
node_id : String
role : String
lifecycle : String
health : String
captured_at : Int32
connection_state : String
last_connected_at : Json
next_retry_at : Json
pending_events : Int32
dropped_events : Int32
sync_cursor : Int32
sync_failures : Int32
last_error_category : Json
negotiated_capabilities : Array[String]
policy_state : String
execution_records : Int32
uncertain_executions : Int32
failed_executions : Int32
succeeded_executions : Int32
} derive(ToJson)
///|
pub extend DiagnosticsJson with ToJson::{to_json}
///|
/// Emits one stable, redacted JSON snapshot for local diagnostics.
pub fn LocalDiagnostics::to_json(
self : LocalDiagnostics,
role~ : RunProfileKind,
) -> String {
let capabilities : Array[String] = []
for capability in self.negotiated_capabilities {
capabilities.push(
match capability {
TransportCapability::EventReplay => "event_replay"
TransportCapability::Heartbeat => "heartbeat"
TransportCapability::Telemetry => "telemetry"
TransportCapability::Command => "command"
TransportCapability::Config => "config"
},
)
}
let summary : DiagnosticsJson = {
node_id: self.node_id.value,
role: match role {
RunProfileKind::Node => "node"
RunProfileKind::ControlPlane => "control_plane"
},
lifecycle: match self.lifecycle {
NodeLifecycle::New => "new"
NodeLifecycle::Enrolled => "enrolled"
NodeLifecycle::Online => "online"
NodeLifecycle::Offline => "offline"
NodeLifecycle::Retired => "retired"
},
health: match self.health {
NodeHealth::Unknown => "unknown"
NodeHealth::Healthy => "healthy"
NodeHealth::Degraded => "degraded"
NodeHealth::Unhealthy => "unhealthy"
},
captured_at: self.captured_at.unix_seconds,
connection_state: match self.connection_state {
ConnectionState::Disconnected => "disconnected"
ConnectionState::Connecting => "connecting"
ConnectionState::Connected => "connected"
ConnectionState::Backoff => "backoff"
},
last_connected_at: match self.last_connected_at {
Some(at) => @json.to_json(at.unix_seconds)
None => Json::null()
},
next_retry_at: match self.next_retry_at {
Some(at) => @json.to_json(at.unix_seconds)
None => Json::null()
},
pending_events: self.pending_events,
dropped_events: self.dropped_events,
sync_cursor: self.sync_cursor.last_acked_sequence,
sync_failures: self.sync_failures,
last_error_category: match self.last_error_category {
Some(category) => @json.to_json(category)
None => Json::null()
},
negotiated_capabilities: capabilities,
policy_state: match self.policy_decision {
PolicyDecision::SafeDefault => "safe_default"
PolicyDecision::Execute(_, _) => "execute"
},
execution_records: self.execution_records,
uncertain_executions: self.uncertain_executions,
failed_executions: self.failed_executions,
succeeded_executions: self.succeeded_executions,
}
@json.to_json(summary).stringify()
}