using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace StellaOps.Policy.Engine.WhatIfSimulation;
///
/// Request for what-if simulation supporting hypothetical SBOM diffs and draft policies.
///
public sealed record WhatIfSimulationRequest
{
///
/// Tenant identifier.
///
[JsonPropertyName("tenant_id")]
public required string TenantId { get; init; }
///
/// Base snapshot ID to apply diffs to.
///
[JsonPropertyName("base_snapshot_id")]
public required string BaseSnapshotId { get; init; }
///
/// Active policy pack ID to use as baseline.
/// If DraftPolicy is provided, this will be compared against.
///
[JsonPropertyName("baseline_pack_id")]
public string? BaselinePackId { get; init; }
///
/// Baseline policy version. If null, uses active version.
///
[JsonPropertyName("baseline_pack_version")]
public int? BaselinePackVersion { get; init; }
///
/// Draft policy to simulate (not yet activated).
/// If null, uses baseline policy.
///
[JsonPropertyName("draft_policy")]
public WhatIfDraftPolicy? DraftPolicy { get; init; }
///
/// SBOM diffs to apply hypothetically.
///
[JsonPropertyName("sbom_diffs")]
public ImmutableArray SbomDiffs { get; init; } = ImmutableArray.Empty;
///
/// Specific component PURLs to evaluate. If empty, evaluates affected by diffs.
///
[JsonPropertyName("target_purls")]
public ImmutableArray TargetPurls { get; init; } = ImmutableArray.Empty;
///
/// Maximum number of components to evaluate.
///
[JsonPropertyName("limit")]
public int Limit { get; init; } = 1000;
///
/// Whether to include detailed explanations for each decision.
///
[JsonPropertyName("include_explanations")]
public bool IncludeExplanations { get; init; } = false;
///
/// Correlation ID for tracing.
///
[JsonPropertyName("correlation_id")]
public string? CorrelationId { get; init; }
}
///
/// Draft policy definition for simulation.
///
public sealed record WhatIfDraftPolicy
{
///
/// Draft policy pack ID.
///
[JsonPropertyName("pack_id")]
public required string PackId { get; init; }
///
/// Draft policy version.
///
[JsonPropertyName("version")]
public int Version { get; init; }
///
/// Raw YAML policy definition to compile and evaluate.
/// If provided, this is compiled on-the-fly.
///
[JsonPropertyName("policy_yaml")]
public string? PolicyYaml { get; init; }
///
/// Pre-compiled bundle digest if available.
///
[JsonPropertyName("bundle_digest")]
public string? BundleDigest { get; init; }
}
///
/// Hypothetical SBOM modification for what-if simulation.
///
public sealed record WhatIfSbomDiff
{
///
/// Type of modification: add, remove, upgrade, downgrade.
///
[JsonPropertyName("operation")]
public required string Operation { get; init; }
///
/// Component PURL being modified.
///
[JsonPropertyName("purl")]
public required string Purl { get; init; }
///
/// New version for upgrade/downgrade operations.
///
[JsonPropertyName("new_version")]
public string? NewVersion { get; init; }
///
/// Original version (for reference in upgrades/downgrades).
///
[JsonPropertyName("original_version")]
public string? OriginalVersion { get; init; }
///
/// Hypothetical advisory IDs affecting this component.
///
[JsonPropertyName("advisory_ids")]
public ImmutableArray AdvisoryIds { get; init; } = ImmutableArray.Empty;
///
/// Hypothetical VEX status for this component.
///
[JsonPropertyName("vex_status")]
public string? VexStatus { get; init; }
///
/// Hypothetical reachability state.
///
[JsonPropertyName("reachability")]
public string? Reachability { get; init; }
}
///
/// Response from what-if simulation.
///
public sealed record WhatIfSimulationResponse
{
///
/// Simulation identifier.
///
[JsonPropertyName("simulation_id")]
public required string SimulationId { get; init; }
///
/// Tenant identifier.
///
[JsonPropertyName("tenant_id")]
public required string TenantId { get; init; }
///
/// Base snapshot ID used.
///
[JsonPropertyName("base_snapshot_id")]
public required string BaseSnapshotId { get; init; }
///
/// Baseline policy used for comparison.
///
[JsonPropertyName("baseline_policy")]
public required WhatIfPolicyRef BaselinePolicy { get; init; }
///
/// Simulated policy (draft or modified).
///
[JsonPropertyName("simulated_policy")]
public WhatIfPolicyRef? SimulatedPolicy { get; init; }
///
/// Decision changes between baseline and simulation.
///
[JsonPropertyName("decision_changes")]
public required ImmutableArray DecisionChanges { get; init; }
///
/// Summary of changes.
///
[JsonPropertyName("summary")]
public required WhatIfSummary Summary { get; init; }
///
/// When the simulation was executed.
///
[JsonPropertyName("executed_at")]
public required DateTimeOffset ExecutedAt { get; init; }
///
/// Execution duration in milliseconds.
///
[JsonPropertyName("duration_ms")]
public long DurationMs { get; init; }
///
/// Correlation ID.
///
[JsonPropertyName("correlation_id")]
public string? CorrelationId { get; init; }
}
///
/// Policy reference in simulation.
///
public sealed record WhatIfPolicyRef(
[property: JsonPropertyName("pack_id")] string PackId,
[property: JsonPropertyName("version")] int Version,
[property: JsonPropertyName("bundle_digest")] string? BundleDigest,
[property: JsonPropertyName("is_draft")] bool IsDraft);
///
/// A decision change detected in what-if simulation.
///
public sealed record WhatIfDecisionChange
{
///
/// Component PURL.
///
[JsonPropertyName("purl")]
public required string Purl { get; init; }
///
/// Advisory ID if applicable.
///
[JsonPropertyName("advisory_id")]
public string? AdvisoryId { get; init; }
///
/// Type of change: new, removed, status_changed, severity_changed.
///
[JsonPropertyName("change_type")]
public required string ChangeType { get; init; }
///
/// Baseline decision.
///
[JsonPropertyName("baseline")]
public WhatIfDecision? Baseline { get; init; }
///
/// Simulated decision.
///
[JsonPropertyName("simulated")]
public WhatIfDecision? Simulated { get; init; }
///
/// SBOM diff that caused this change, if any.
///
[JsonPropertyName("caused_by_diff")]
public WhatIfSbomDiff? CausedByDiff { get; init; }
///
/// Explanation for the change.
///
[JsonPropertyName("explanation")]
public WhatIfExplanation? Explanation { get; init; }
}
///
/// A decision in what-if simulation.
///
public sealed record WhatIfDecision(
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("severity")] string? Severity,
[property: JsonPropertyName("rule_name")] string? RuleName,
[property: JsonPropertyName("priority")] int? Priority,
[property: JsonPropertyName("exception_applied")] bool ExceptionApplied);
///
/// Explanation for a what-if decision.
///
public sealed record WhatIfExplanation
{
///
/// Rules that matched.
///
[JsonPropertyName("matched_rules")]
public ImmutableArray MatchedRules { get; init; } = ImmutableArray.Empty;
///
/// Key factors in the decision.
///
[JsonPropertyName("factors")]
public ImmutableArray Factors { get; init; } = ImmutableArray.Empty;
///
/// VEX evidence considered.
///
[JsonPropertyName("vex_evidence")]
public string? VexEvidence { get; init; }
///
/// Reachability state.
///
[JsonPropertyName("reachability")]
public string? Reachability { get; init; }
}
///
/// Summary of what-if simulation results.
///
public sealed record WhatIfSummary
{
///
/// Total components evaluated.
///
[JsonPropertyName("total_evaluated")]
public int TotalEvaluated { get; init; }
///
/// Components with changed decisions.
///
[JsonPropertyName("total_changed")]
public int TotalChanged { get; init; }
///
/// Components newly affected.
///
[JsonPropertyName("newly_affected")]
public int NewlyAffected { get; init; }
///
/// Components no longer affected.
///
[JsonPropertyName("no_longer_affected")]
public int NoLongerAffected { get; init; }
///
/// Status changes by type.
///
[JsonPropertyName("status_changes")]
public required ImmutableDictionary StatusChanges { get; init; }
///
/// Severity changes by type (e.g., "low_to_high").
///
[JsonPropertyName("severity_changes")]
public required ImmutableDictionary SeverityChanges { get; init; }
///
/// Impact assessment.
///
[JsonPropertyName("impact")]
public required WhatIfImpact Impact { get; init; }
}
///
/// Impact assessment from what-if simulation.
///
public sealed record WhatIfImpact(
[property: JsonPropertyName("risk_delta")] string RiskDelta, // increased, decreased, unchanged
[property: JsonPropertyName("blocked_count_delta")] int BlockedCountDelta,
[property: JsonPropertyName("warning_count_delta")] int WarningCountDelta,
[property: JsonPropertyName("recommendation")] string? Recommendation);