namespace StellaOps.Policy.Explainability; /// /// Structured verdict rationale following the 4-line template. /// Line 1: Evidence summary /// Line 2: Policy clause that triggered the decision /// Line 3: Attestations and proofs supporting the verdict /// Line 4: Final decision with score and recommendation /// public sealed record VerdictRationale { /// Schema version for forward compatibility. [JsonPropertyName("schema_version")] public string SchemaVersion { get; init; } = "1.0"; /// Unique rationale ID (content-addressed). [JsonPropertyName("rationale_id")] public required string RationaleId { get; init; } /// Reference to the verdict being explained. [JsonPropertyName("verdict_ref")] public required VerdictReference VerdictRef { get; init; } /// Line 1: Evidence summary. [JsonPropertyName("evidence")] public required RationaleEvidence Evidence { get; init; } /// Line 2: Policy clause that triggered the decision. [JsonPropertyName("policy_clause")] public required RationalePolicyClause PolicyClause { get; init; } /// Line 3: Attestations and proofs supporting the verdict. [JsonPropertyName("attestations")] public required RationaleAttestations Attestations { get; init; } /// Line 4: Final decision with score and recommendation. [JsonPropertyName("decision")] public required RationaleDecision Decision { get; init; } /// Generation timestamp (UTC). [JsonPropertyName("generated_at")] public required DateTimeOffset GeneratedAt { get; init; } /// Input digests for reproducibility. [JsonPropertyName("input_digests")] public required RationaleInputDigests InputDigests { get; init; } } /// Reference to the verdict being explained. public sealed record VerdictReference { [JsonPropertyName("attestation_id")] public required string AttestationId { get; init; } [JsonPropertyName("artifact_digest")] public required string ArtifactDigest { get; init; } [JsonPropertyName("policy_id")] public required string PolicyId { get; init; } [JsonPropertyName("cve")] public string? Cve { get; init; } [JsonPropertyName("component_purl")] public string? ComponentPurl { get; init; } } /// Line 1: Evidence summary. public sealed record RationaleEvidence { [JsonPropertyName("cve")] public required string Cve { get; init; } [JsonPropertyName("component")] public required ComponentIdentity Component { get; init; } [JsonPropertyName("reachability")] public ReachabilityDetail? Reachability { get; init; } [JsonPropertyName("formatted_text")] public required string FormattedText { get; init; } } public sealed record ComponentIdentity { [JsonPropertyName("purl")] public required string Purl { get; init; } [JsonPropertyName("name")] public string? Name { get; init; } [JsonPropertyName("version")] public string? Version { get; init; } [JsonPropertyName("ecosystem")] public string? Ecosystem { get; init; } } public sealed record ReachabilityDetail { [JsonPropertyName("vulnerable_function")] public string? VulnerableFunction { get; init; } [JsonPropertyName("entry_point")] public string? EntryPoint { get; init; } [JsonPropertyName("path_summary")] public string? PathSummary { get; init; } } /// Line 2: Policy clause reference. public sealed record RationalePolicyClause { [JsonPropertyName("clause_id")] public required string ClauseId { get; init; } [JsonPropertyName("rule_description")] public required string RuleDescription { get; init; } [JsonPropertyName("conditions")] public required IReadOnlyList Conditions { get; init; } [JsonPropertyName("formatted_text")] public required string FormattedText { get; init; } } /// Line 3: Attestations and proofs. public sealed record RationaleAttestations { [JsonPropertyName("path_witness")] public AttestationReference? PathWitness { get; init; } [JsonPropertyName("vex_statements")] public IReadOnlyList? VexStatements { get; init; } [JsonPropertyName("provenance")] public AttestationReference? Provenance { get; init; } [JsonPropertyName("formatted_text")] public required string FormattedText { get; init; } } public sealed record AttestationReference { [JsonPropertyName("id")] public required string Id { get; init; } [JsonPropertyName("type")] public required string Type { get; init; } [JsonPropertyName("digest")] public string? Digest { get; init; } [JsonPropertyName("summary")] public string? Summary { get; init; } } /// Line 4: Final decision. public sealed record RationaleDecision { [JsonPropertyName("verdict")] public required string Verdict { get; init; } [JsonPropertyName("score")] public double? Score { get; init; } [JsonPropertyName("recommendation")] public required string Recommendation { get; init; } [JsonPropertyName("mitigation")] public MitigationGuidance? Mitigation { get; init; } [JsonPropertyName("formatted_text")] public required string FormattedText { get; init; } } public sealed record MitigationGuidance { [JsonPropertyName("action")] public required string Action { get; init; } [JsonPropertyName("details")] public string? Details { get; init; } } /// Input digests for reproducibility. public sealed record RationaleInputDigests { [JsonPropertyName("verdict_digest")] public required string VerdictDigest { get; init; } [JsonPropertyName("policy_digest")] public string? PolicyDigest { get; init; } [JsonPropertyName("evidence_digest")] public string? EvidenceDigest { get; init; } }