feat: integrate chat voice and diagnostics client
This commit is contained in:
@@ -61,6 +61,14 @@ pub enum DiagnosticsError {
|
||||
/// to the PoC value so audit grep patterns survive the promotion.
|
||||
pub const REDACTION_MARKER: &str = "[REDACTED]";
|
||||
|
||||
/// SRS-122: In-memory log capacity for release builds.
|
||||
/// Release builds use a smaller buffer to limit memory footprint and residual data in exports.
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const DEFAULT_LOG_CAPACITY: usize = 256;
|
||||
/// SRS-122: In-memory log capacity for debug builds.
|
||||
#[cfg(debug_assertions)]
|
||||
pub const DEFAULT_LOG_CAPACITY: usize = 4096;
|
||||
|
||||
/// Registry of known-secret values that must never appear in logs
|
||||
/// or exports. Cross-spike contract per SS-AUD-003: the secure
|
||||
/// storage adapter calls [`Self::register`] every time a secret
|
||||
@@ -612,6 +620,10 @@ pub struct DiagnosticExport {
|
||||
/// fragment contains only device-side technical scalars per
|
||||
/// SDD-090 (no PII, no permission state, no server identity).
|
||||
pub android_audio: Option<String>,
|
||||
/// SRS-100: Network connectivity diagnostics summary.
|
||||
pub network_info: Option<String>,
|
||||
/// SRS-097: Protocol event recording trace.
|
||||
pub protocol_events: Vec<String>,
|
||||
}
|
||||
|
||||
impl DiagnosticExport {
|
||||
@@ -625,6 +637,8 @@ impl DiagnosticExport {
|
||||
recent_logs: sink.snapshot(),
|
||||
known_secret_count: sink.redactor().secrets().len(),
|
||||
android_audio: None,
|
||||
network_info: None,
|
||||
protocol_events: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -638,6 +652,18 @@ impl DiagnosticExport {
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach a network connectivity diagnostics fragment (SRS-100).
|
||||
pub fn with_network_info(mut self, info: Option<String>) -> Self {
|
||||
self.network_info = info;
|
||||
self
|
||||
}
|
||||
|
||||
/// Attach protocol event trace (SRS-097).
|
||||
pub fn with_protocol_events(mut self, events: Vec<String>) -> Self {
|
||||
self.protocol_events = events;
|
||||
self
|
||||
}
|
||||
|
||||
/// Render as a plaintext blob suitable for `Share` / `Copy`.
|
||||
/// The output is multi-line UTF-8, redacted.
|
||||
pub fn to_text(&self) -> String {
|
||||
@@ -656,6 +682,17 @@ impl DiagnosticExport {
|
||||
out.push_str("\n[audio.android]\n");
|
||||
out.push_str(yaml);
|
||||
}
|
||||
if let Some(info) = &self.network_info {
|
||||
out.push_str("\n[network]\n");
|
||||
out.push_str(info);
|
||||
}
|
||||
if !self.protocol_events.is_empty() {
|
||||
out.push_str("\n[protocol events]\n");
|
||||
for ev in &self.protocol_events {
|
||||
out.push_str(ev);
|
||||
out.push('\n');
|
||||
}
|
||||
}
|
||||
out.push_str("\n[recent logs]\n");
|
||||
for line in &self.recent_logs {
|
||||
out.push_str(line);
|
||||
@@ -665,6 +702,86 @@ impl DiagnosticExport {
|
||||
}
|
||||
}
|
||||
|
||||
/// SRS-097/098: Ring-buffer recorder of protocol-level events
|
||||
/// (connect, disconnect, snapshot changes, channel joins) for
|
||||
/// diagnostic export and state-sync replay verification.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProtocolEventRecorder {
|
||||
events: Vec<String>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl ProtocolEventRecorder {
|
||||
/// Create a recorder with the given ring-buffer capacity.
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
events: Vec::with_capacity(capacity),
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, ts: &str, kind: &str, detail: &str) {
|
||||
let s = format!("[{ts}] {kind}: {detail}");
|
||||
if self.events.len() >= self.capacity {
|
||||
self.events.remove(0);
|
||||
}
|
||||
self.events.push(s);
|
||||
}
|
||||
|
||||
/// Record a successful connection.
|
||||
pub fn record_connected(&mut self, server_name: &str) {
|
||||
self.push("connect", "connected", server_name);
|
||||
}
|
||||
|
||||
/// Record a graceful or forced disconnect.
|
||||
pub fn record_disconnected(&mut self, reason: &str) {
|
||||
self.push("disconnect", "disconnected", reason);
|
||||
}
|
||||
|
||||
/// Record a reconnect attempt.
|
||||
pub fn record_reconnecting(&mut self, attempt: u64, delay_secs: u64) {
|
||||
self.push(
|
||||
"reconnect",
|
||||
"reconnecting",
|
||||
&format!("attempt={attempt} delay={delay_secs}s"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Record a snapshot tree change.
|
||||
pub fn record_snapshot_changed(&mut self, channels: usize, clients: usize) {
|
||||
self.push(
|
||||
"snapshot",
|
||||
"changed",
|
||||
&format!("channels={channels} clients={clients}"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Record a channel join event.
|
||||
pub fn record_channel_join(&mut self, channel_id: u64, channel_name: &str) {
|
||||
self.push(
|
||||
"join",
|
||||
"channel_joined",
|
||||
&format!("id={channel_id} name={channel_name}"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Record a platform lifecycle transition (SRS-138).
|
||||
pub fn record_lifecycle(&mut self, state: &str) {
|
||||
self.push("lifecycle", state, "");
|
||||
}
|
||||
|
||||
/// Drain all recorded events and reset the buffer.
|
||||
pub fn drain(&mut self) -> Vec<String> {
|
||||
std::mem::take(&mut self.events)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProtocolEventRecorder {
|
||||
fn default() -> Self {
|
||||
Self::new(256)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user