// -----------------------------------------------------------------------------
// ReplayTelemetry.cs
// Sprint: SPRINT_1227_0005_0004_BE_verdict_replay
// Task: T10 — Telemetry for replay outcomes
// -----------------------------------------------------------------------------
using System.Diagnostics;
using System.Diagnostics.Metrics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace StellaOps.AuditPack.Services;
///
/// OpenTelemetry instrumentation for verdict replay operations.
/// Provides metrics, traces, and structured logging support.
///
public sealed class ReplayTelemetry : IDisposable
{
///
/// Service name for telemetry identification.
///
public const string ServiceName = "StellaOps.Replay";
///
/// Meter name for replay metrics.
///
public const string MeterName = "StellaOps.Replay";
///
/// Activity source name for replay tracing.
///
public const string ActivitySourceName = "StellaOps.Replay";
private readonly Meter _meter;
// Counters
private readonly Counter _replayExecutionsTotal;
private readonly Counter _replayMatchesTotal;
private readonly Counter _replayDivergencesTotal;
private readonly Counter _replayErrorsTotal;
private readonly Counter _attestationsGeneratedTotal;
private readonly Counter _attestationsVerifiedTotal;
private readonly Counter _eligibilityChecksTotal;
// Histograms
private readonly Histogram _replayDurationMs;
private readonly Histogram _attestationGenerationDurationMs;
private readonly Histogram _driftCount;
private readonly Histogram _confidenceScore;
// Gauges
private readonly UpDownCounter _replaysInProgress;
///
/// Activity source for distributed tracing.
///
public static readonly ActivitySource ActivitySource = new(ActivitySourceName);
///
/// Initializes a new instance of the ReplayTelemetry class.
///
public ReplayTelemetry(IMeterFactory? meterFactory = null)
{
_meter = meterFactory?.Create(MeterName) ?? new Meter(MeterName);
// Counters
_replayExecutionsTotal = _meter.CreateCounter(
"stellaops.replay.executions.total",
unit: "{execution}",
description: "Total number of replay executions");
_replayMatchesTotal = _meter.CreateCounter(
"stellaops.replay.matches.total",
unit: "{match}",
description: "Total number of replay matches (verdict unchanged)");
_replayDivergencesTotal = _meter.CreateCounter(
"stellaops.replay.divergences.total",
unit: "{divergence}",
description: "Total number of replay divergences detected");
_replayErrorsTotal = _meter.CreateCounter(
"stellaops.replay.errors.total",
unit: "{error}",
description: "Total number of replay errors");
_attestationsGeneratedTotal = _meter.CreateCounter(
"stellaops.replay.attestations.generated.total",
unit: "{attestation}",
description: "Total number of replay attestations generated");
_attestationsVerifiedTotal = _meter.CreateCounter(
"stellaops.replay.attestations.verified.total",
unit: "{verification}",
description: "Total number of replay attestations verified");
_eligibilityChecksTotal = _meter.CreateCounter(
"stellaops.replay.eligibility.checks.total",
unit: "{check}",
description: "Total number of replay eligibility checks");
// Histograms
_replayDurationMs = _meter.CreateHistogram(
"stellaops.replay.duration.ms",
unit: "ms",
description: "Replay execution duration in milliseconds");
_attestationGenerationDurationMs = _meter.CreateHistogram(
"stellaops.replay.attestation.generation.duration.ms",
unit: "ms",
description: "Attestation generation duration in milliseconds");
_driftCount = _meter.CreateHistogram(
"stellaops.replay.drift.count",
unit: "{drift}",
description: "Number of drifts detected per replay");
_confidenceScore = _meter.CreateHistogram(
"stellaops.replay.eligibility.confidence",
unit: "1",
description: "Replay eligibility confidence score distribution");
// Gauges
_replaysInProgress = _meter.CreateUpDownCounter(
"stellaops.replay.in_progress",
unit: "{replay}",
description: "Number of replays currently in progress");
}
#region Replay Execution Metrics
///
/// Records the start of a replay execution.
///
public void RecordReplayStarted(string manifestId, string scanId)
{
_replaysInProgress.Add(1, new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.ScanId, scanId }
});
}
///
/// Records the completion of a replay execution.
///
public void RecordReplayCompleted(
string manifestId,
string scanId,
ReplayOutcome outcome,
int driftCount,
TimeSpan duration)
{
var tags = new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.ScanId, scanId },
{ ReplayTelemetryTags.Outcome, outcome.ToString().ToLowerInvariant() }
};
_replaysInProgress.Add(-1, new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.ScanId, scanId }
});
_replayExecutionsTotal.Add(1, tags);
_replayDurationMs.Record(duration.TotalMilliseconds, tags);
switch (outcome)
{
case ReplayOutcome.Match:
_replayMatchesTotal.Add(1, tags);
break;
case ReplayOutcome.Divergence:
_replayDivergencesTotal.Add(1, tags);
_driftCount.Record(driftCount, tags);
break;
case ReplayOutcome.Error:
_replayErrorsTotal.Add(1, tags);
break;
}
}
///
/// Records a replay error.
///
public void RecordReplayError(
string manifestId,
string scanId,
string errorCode)
{
var tags = new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.ScanId, scanId },
{ ReplayTelemetryTags.ErrorCode, errorCode }
};
_replaysInProgress.Add(-1, new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.ScanId, scanId }
});
_replayErrorsTotal.Add(1, tags);
}
#endregion
#region Attestation Metrics
///
/// Records attestation generation.
///
public void RecordAttestationGenerated(
string manifestId,
bool match,
TimeSpan duration)
{
var tags = new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.Match, match.ToString().ToLowerInvariant() }
};
_attestationsGeneratedTotal.Add(1, tags);
_attestationGenerationDurationMs.Record(duration.TotalMilliseconds, tags);
}
///
/// Records attestation verification.
///
public void RecordAttestationVerified(
string attestationId,
bool valid)
{
var tags = new TagList
{
{ ReplayTelemetryTags.AttestationId, attestationId },
{ ReplayTelemetryTags.Valid, valid.ToString().ToLowerInvariant() }
};
_attestationsVerifiedTotal.Add(1, tags);
}
#endregion
#region Eligibility Metrics
///
/// Records an eligibility check.
///
public void RecordEligibilityCheck(
string manifestId,
bool eligible,
double confidenceScore)
{
var tags = new TagList
{
{ ReplayTelemetryTags.ManifestId, manifestId },
{ ReplayTelemetryTags.Eligible, eligible.ToString().ToLowerInvariant() }
};
_eligibilityChecksTotal.Add(1, tags);
_confidenceScore.Record(confidenceScore, tags);
}
#endregion
#region Activity Helpers
///
/// Starts an activity for replay execution.
///
public static Activity? StartReplayActivity(string manifestId, string scanId)
{
var activity = ActivitySource.StartActivity("Replay.Execute");
activity?.SetTag(ReplayTelemetryTags.ManifestId, manifestId);
activity?.SetTag(ReplayTelemetryTags.ScanId, scanId);
return activity;
}
///
/// Starts an activity for attestation generation.
///
public static Activity? StartAttestationActivity(string manifestId)
{
var activity = ActivitySource.StartActivity("Replay.GenerateAttestation");
activity?.SetTag(ReplayTelemetryTags.ManifestId, manifestId);
return activity;
}
///
/// Starts an activity for eligibility check.
///
public static Activity? StartEligibilityActivity(string manifestId)
{
var activity = ActivitySource.StartActivity("Replay.CheckEligibility");
activity?.SetTag(ReplayTelemetryTags.ManifestId, manifestId);
return activity;
}
///
/// Starts an activity for divergence detection.
///
public static Activity? StartDivergenceActivity(string manifestId)
{
var activity = ActivitySource.StartActivity("Replay.DetectDivergence");
activity?.SetTag(ReplayTelemetryTags.ManifestId, manifestId);
return activity;
}
#endregion
///
public void Dispose()
{
_meter.Dispose();
}
}
///
/// Tag names for replay telemetry.
///
public static class ReplayTelemetryTags
{
public const string ManifestId = "manifest_id";
public const string ScanId = "scan_id";
public const string BundleId = "bundle_id";
public const string AttestationId = "attestation_id";
public const string Outcome = "outcome";
public const string Match = "match";
public const string Valid = "valid";
public const string Eligible = "eligible";
public const string ErrorCode = "error_code";
public const string DivergenceType = "divergence_type";
public const string DriftType = "drift_type";
public const string Severity = "severity";
}
///
/// Replay outcome values.
///
public enum ReplayOutcome
{
/// Verdict matched the original.
Match,
/// Divergence detected between original and replayed verdict.
Divergence,
/// Replay execution failed with error.
Error,
/// Replay was cancelled.
Cancelled
}
///
/// Divergence severity levels.
///
public static class DivergenceSeverities
{
public const string Critical = "critical";
public const string High = "high";
public const string Medium = "medium";
public const string Low = "low";
public const string Info = "info";
}
///
/// Divergence type values.
///
public static class DivergenceTypes
{
public const string VerdictDigest = "verdict_digest";
public const string Decision = "decision";
public const string Confidence = "confidence";
public const string Input = "input";
public const string Policy = "policy";
public const string Evidence = "evidence";
}
///
/// Extension methods for adding replay telemetry.
///
public static class ReplayTelemetryExtensions
{
///
/// Adds replay OpenTelemetry instrumentation.
///
public static IServiceCollection AddReplayTelemetry(this IServiceCollection services)
{
services.TryAddSingleton();
return services;
}
}