function normalizedEvidence(input) {
    return [input.cwd, input.gitBranch, input.topic, input.label]
        .filter((value) => typeof value === 'string')
        .join(' ')
        .toLowerCase();
}
const TOPIC_RULES = [
    { group: 'review', key: 'code-review', label: 'Code review',
        text: /\b(review|audit|pr[-_/ ]?review|code[-_/ ]?review)\b/, tool: /review|comment/ },
    { group: 'ops', key: 'release', label: 'Release',
        text: /\b(release|deploy|publish|rollout|changelog)\b/ },
    { group: 'research', key: 'debugging', label: 'Debugging',
        text: /\b(debug|investigat|repro|root[-_ ]?cause|traceback|stack ?trace)\b/ },
    { group: 'content', key: 'content', label: 'Blog & docs',
        text: /\b(blog|docs?|documentation|readme|post|article|content|caption)\b/ },
    { group: 'research', key: 'research', label: 'Research',
        text: /\b(research|analysis|benchmark|paper|explore|spike)\b/, tool: /web_search|search_query|webfetch|browser/ },
    { group: 'code', key: 'bugfix', label: 'Bug fixes',
        text: /\b(fix|bug|hotfix|patch)\b/ },
    { group: 'code', key: 'refactor', label: 'Refactor',
        text: /\b(refactor|cleanup|clean[-_ ]?up|restructure|rename|tidy)\b/ },
    { group: 'ops', key: 'operations', label: 'Fleet / ops',
        text: /\b(fleet|ci|infra|daemon|provision|ops|pipeline)\b/, tool: /ssh|deploy|computer/ },
    { group: 'code', key: 'feature', label: 'Feature work',
        text: /\b(feat|feature|implement|add|build|code|test)\b/, tool: /edit|write|apply_patch|exec|bash|shell/ },
];
/** Classify a session from metadata and aggregate tool names; never reads transcript text. */
export function classifyTopic(input) {
    const text = normalizedEvidence(input);
    const tools = Object.keys(input.toolMix ?? {}).map((tool) => tool.toLowerCase());
    for (const rule of TOPIC_RULES) {
        if ((rule.text && rule.text.test(text)) || (rule.tool && tools.some((tool) => rule.tool.test(tool)))) {
            return { group: rule.group, key: rule.key, label: rule.label };
        }
    }
    return { group: 'research', key: 'general', label: 'General' };
}
/** Bucket a failed tool call without inspecting raw tool input or transcript text. */
export function classifyCause(call) {
    const evidence = [call.error_code, call.error, call.parse_error]
        .filter((value) => typeof value === 'string')
        .join(' ')
        .toLowerCase();
    if (/\b(?:git-guard|main-branch-guard)\b/.test(evidence))
        return 'guard';
    if (/permission\b.*\bdenied\b.*\b(?:auto[- ]mode|classifier)\b/.test(evidence)) {
        return 'hook';
    }
    return 'real';
}
const DRIFT_THRESHOLD = 0.20;
/**
 * Compare today's per-bucket stats to the last 7 days of history and return
 * movement signals. Buckets with fewer than 3 historical days are skipped —
 * not enough signal to distinguish noise from drift.
 */
export function computeDriftSignal(history, today) {
    const signals = [];
    for (const stat of today) {
        const pastStats = history
            .flatMap((day) => day.filter((b) => b.key === stat.key))
            .slice(-7);
        if (pastStats.length < 3)
            continue;
        const avgError = pastStats.reduce((sum, b) => sum + b.errorRate, 0) / pastStats.length;
        const avgStall = pastStats.reduce((sum, b) => sum + b.stallRate, 0) / pastStats.length;
        const errorDelta = stat.errorRate - avgError;
        const stallDelta = stat.stallRate - avgStall;
        const severity = errorDelta > DRIFT_THRESHOLD || stallDelta > DRIFT_THRESHOLD
            ? 'degrading'
            : errorDelta < -DRIFT_THRESHOLD || stallDelta < -DRIFT_THRESHOLD
                ? 'improving'
                : 'stable';
        signals.push({ bucket: stat.key, errorDelta, stallDelta, severity });
    }
    return signals.sort((a, b) => b.errorDelta - a.errorDelta);
}
