aicx 0.9.2

Operator CLI + MCP server: canonical corpus first, optional semantic index second (Claude Code, Codex, Gemini)
Documentation
#![allow(unused_imports)]
use super::files::MAX_LINE_BYTES;
use super::*;

pub(crate) fn describe_content_sanitization_warning(
    warning: &sanitize::ContentSanitizationWarning,
) -> String {
    match warning {
        sanitize::ContentSanitizationWarning::NullByteStripped(offset) => {
            format!("stripped NUL byte at byte offset {offset}")
        }
        sanitize::ContentSanitizationWarning::BidiOverride(ch, offset) => format!(
            "preserved bidi override U+{:04X} at byte offset {}",
            *ch as u32, offset
        ),
        sanitize::ContentSanitizationWarning::ZeroWidth(ch, offset) => format!(
            "preserved zero-width character U+{:04X} at byte offset {}",
            *ch as u32, offset
        ),
    }
}

pub(crate) fn push_unique_sample(samples: &mut Vec<String>, sample: String, max: usize) {
    if samples.len() < max && !samples.iter().any(|existing| existing == &sample) {
        samples.push(sample);
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ClaudeSessionWarning {
    MissingSessionId {
        fallback: String,
    },
    SessionIdDrift {
        first: String,
        ignored: Vec<String>,
    },
    UnparsableTimestamp {
        count: usize,
        samples: Vec<String>,
    },
    FallbackTimestamp {
        count: usize,
        samples: Vec<String>,
    },
    InvalidEpochMillis {
        count: usize,
        samples: Vec<String>,
    },
    OversizedLine {
        count: usize,
        samples: Vec<String>,
    },
    ContentSanitization {
        warning: sanitize::ContentSanitizationWarning,
    },
}

impl ClaudeSessionWarning {
    pub(crate) fn describe(&self, path: &Path) -> String {
        match self {
            ClaudeSessionWarning::MissingSessionId { fallback } => format!(
                "Claude session warning: {} has no non-empty sessionId; using `{}` fallback",
                path.display(),
                fallback
            ),
            ClaudeSessionWarning::SessionIdDrift { first, ignored } => format!(
                "Claude session warning: {} has multiple sessionId values; using `{}` and ignoring {}",
                path.display(),
                first,
                ignored.join(", ")
            ),
            ClaudeSessionWarning::UnparsableTimestamp { count, samples } => format!(
                "Claude session warning: {} has {} unparsable timestamp(s); frames dropped. Sample(s): {}",
                path.display(),
                count,
                samples.join(", ")
            ),
            ClaudeSessionWarning::FallbackTimestamp { count, samples } => format!(
                "Claude session warning: {} has {} frames preserved with fallback timestamp; sample lines: {}",
                path.display(),
                count,
                samples.join(", ")
            ),
            ClaudeSessionWarning::InvalidEpochMillis { count, samples } => format!(
                "Claude history warning: {} has {} invalid epoch millisecond timestamp(s); frames dropped. Sample(s): {}",
                path.display(),
                count,
                samples.join(", ")
            ),
            ClaudeSessionWarning::OversizedLine { count, samples } => format!(
                "Claude session warning: {} skipped {} oversized JSONL line(s) over {} bytes. Sample(s): {}",
                path.display(),
                count,
                MAX_LINE_BYTES,
                samples.join(", ")
            ),
            ClaudeSessionWarning::ContentSanitization { warning } => format!(
                "Claude content warning: {} {}",
                path.display(),
                describe_content_sanitization_warning(warning)
            ),
        }
    }
}

pub(crate) fn emit_claude_session_warnings(path: &Path, warnings: &[ClaudeSessionWarning]) {
    use crate::diagnostics::{self, DiagnosticKind};
    for warning in warnings {
        let line = warning.describe(path);
        diagnostics::log_describe(&line);
        match warning {
            ClaudeSessionWarning::FallbackTimestamp { count, .. } => {
                diagnostics::record("claude", DiagnosticKind::FallbackTimestamp, *count, path);
            }
            ClaudeSessionWarning::UnparsableTimestamp { count, .. } => {
                diagnostics::record("claude", DiagnosticKind::UnparsableTimestamp, *count, path);
            }
            ClaudeSessionWarning::InvalidEpochMillis { count, .. } => {
                diagnostics::record("claude", DiagnosticKind::InvalidEpochMillis, *count, path);
            }
            ClaudeSessionWarning::OversizedLine { count, .. } => {
                diagnostics::record("claude", DiagnosticKind::OversizedLine, *count, path);
            }
            ClaudeSessionWarning::MissingSessionId { .. } => {
                diagnostics::record("claude", DiagnosticKind::MissingSessionId, 1, path);
            }
            ClaudeSessionWarning::SessionIdDrift { .. } => {
                diagnostics::record("claude", DiagnosticKind::SessionIdDrift, 1, path);
            }
            ClaudeSessionWarning::ContentSanitization { warning } => {
                record_content_sanitization("claude", warning, path);
            }
        }
        if diagnostics::is_verbose() {
            eprintln!("{line}");
        }
    }
}

pub(crate) fn record_content_sanitization(
    extractor: &'static str,
    warning: &sanitize::ContentSanitizationWarning,
    path: &Path,
) {
    use crate::diagnostics::{self, DiagnosticKind};
    let kind = match warning {
        sanitize::ContentSanitizationWarning::BidiOverride(_, _) => DiagnosticKind::BidiOverride,
        sanitize::ContentSanitizationWarning::ZeroWidth(_, _) => DiagnosticKind::ZeroWidth,
        sanitize::ContentSanitizationWarning::NullByteStripped(_) => {
            DiagnosticKind::NullByteStripped
        }
    };
    diagnostics::record(extractor, kind, 1, path);
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum GeminiSessionWarning {
    MissingSessionId {
        fallback: String,
    },
    SessionIdDrift {
        first: String,
        ignored: Vec<String>,
    },
    UnparsableTimestamp {
        count: usize,
        samples: Vec<String>,
    },
    UnknownMsgType {
        count: usize,
        samples: Vec<String>,
    },
    ContentSanitization {
        warning: sanitize::ContentSanitizationWarning,
    },
}

impl GeminiSessionWarning {
    pub(crate) fn describe(&self, path: &Path) -> String {
        match self {
            GeminiSessionWarning::MissingSessionId { fallback } => format!(
                "Gemini session warning: {} has no non-empty sessionId; using `{}` fallback",
                path.display(),
                fallback
            ),
            GeminiSessionWarning::SessionIdDrift { first, ignored } => format!(
                "Gemini session warning: {} has multiple sessionId values; using `{}` and ignoring {}",
                path.display(),
                first,
                ignored.join(", ")
            ),
            GeminiSessionWarning::UnparsableTimestamp { count, samples } => format!(
                "Gemini session warning: {} has {} unparsable timestamp(s); frames dropped or fell back to parent timestamp. Sample(s): {}",
                path.display(),
                count,
                samples.join(", ")
            ),
            GeminiSessionWarning::UnknownMsgType { count, samples } => format!(
                "Gemini session warning: {} encountered {} message(s) with unrecognized type/role; preserved as system_note. Sample(s): {}",
                path.display(),
                count,
                samples.join(", ")
            ),
            GeminiSessionWarning::ContentSanitization { warning } => format!(
                "Gemini content warning: {} {}",
                path.display(),
                describe_content_sanitization_warning(warning)
            ),
        }
    }
}

pub(crate) fn emit_gemini_session_warnings(path: &Path, warnings: &[GeminiSessionWarning]) {
    use crate::diagnostics::{self, DiagnosticKind};
    for warning in warnings {
        let line = warning.describe(path);
        diagnostics::log_describe(&line);
        match warning {
            GeminiSessionWarning::UnparsableTimestamp { count, .. } => {
                diagnostics::record("gemini", DiagnosticKind::UnparsableTimestamp, *count, path);
            }
            GeminiSessionWarning::UnknownMsgType { count, .. } => {
                diagnostics::record("gemini", DiagnosticKind::UnknownMsgType, *count, path);
            }
            GeminiSessionWarning::MissingSessionId { .. } => {
                diagnostics::record("gemini", DiagnosticKind::MissingSessionId, 1, path);
            }
            GeminiSessionWarning::SessionIdDrift { .. } => {
                diagnostics::record("gemini", DiagnosticKind::SessionIdDrift, 1, path);
            }
            GeminiSessionWarning::ContentSanitization { warning } => {
                record_content_sanitization("gemini", warning, path);
            }
        }
        if diagnostics::is_verbose() {
            eprintln!("{line}");
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum JunieSessionWarning {
    JunieFallbackId {
        fallback: String,
    },
    OversizedLine {
        count: usize,
        samples: Vec<String>,
    },
    ContentSanitization {
        warning: sanitize::ContentSanitizationWarning,
    },
}

impl JunieSessionWarning {
    pub(crate) fn describe(&self, path: &Path) -> String {
        match self {
            JunieSessionWarning::JunieFallbackId { fallback } => format!(
                "Junie session warning: {} has no session-* ancestor; using `{}` fallback",
                path.display(),
                fallback
            ),
            JunieSessionWarning::OversizedLine { count, samples } => format!(
                "Junie session warning: {} skipped {} oversized JSONL line(s) over {} bytes. Sample(s): {}",
                path.display(),
                count,
                MAX_LINE_BYTES,
                samples.join(", ")
            ),
            JunieSessionWarning::ContentSanitization { warning } => format!(
                "Junie content warning: {} {}",
                path.display(),
                describe_content_sanitization_warning(warning)
            ),
        }
    }
}

pub(crate) fn emit_junie_session_warnings(path: &Path, warnings: &[JunieSessionWarning]) {
    use crate::diagnostics::{self, DiagnosticKind};
    for warning in warnings {
        let line = warning.describe(path);
        diagnostics::log_describe(&line);
        match warning {
            JunieSessionWarning::JunieFallbackId { .. } => {
                diagnostics::record("junie", DiagnosticKind::JunieFallbackId, 1, path);
            }
            JunieSessionWarning::OversizedLine { count, .. } => {
                diagnostics::record("junie", DiagnosticKind::OversizedLine, *count, path);
            }
            JunieSessionWarning::ContentSanitization { warning } => {
                record_content_sanitization("junie", warning, path);
            }
        }
        if diagnostics::is_verbose() {
            eprintln!("{line}");
        }
    }
}

impl PushContentSanitizationWarning for Vec<ClaudeSessionWarning> {
    fn push_content_sanitization_warning(&mut self, warning: sanitize::ContentSanitizationWarning) {
        self.push(ClaudeSessionWarning::ContentSanitization { warning });
    }
}

impl PushContentSanitizationWarning for Vec<GeminiSessionWarning> {
    fn push_content_sanitization_warning(&mut self, warning: sanitize::ContentSanitizationWarning) {
        self.push(GeminiSessionWarning::ContentSanitization { warning });
    }
}

impl PushContentSanitizationWarning for Vec<JunieSessionWarning> {
    fn push_content_sanitization_warning(&mut self, warning: sanitize::ContentSanitizationWarning) {
        self.push(JunieSessionWarning::ContentSanitization { warning });
    }
}