// // Copyright (c) StellaOps. Licensed under the BUSL-1.1. // using System.Collections.Immutable; namespace StellaOps.AdvisoryAI.Actions; /// /// Evaluates whether AI-proposed actions are allowed by policy. /// Integrates with K4 lattice for VEX-aware decisions and approval workflows. /// Sprint: SPRINT_20260109_011_004_BE Task PACT-001 /// public interface IActionPolicyGate { /// /// Evaluates whether an action is allowed by policy. /// /// The action proposal from the AI. /// The execution context including tenant, user, roles, environment. /// Cancellation token. /// The policy decision with any required approvals. Task EvaluateAsync( ActionProposal proposal, ActionContext context, CancellationToken cancellationToken); /// /// Gets a human-readable explanation for a policy decision. /// /// The decision to explain. /// Cancellation token. /// Human-readable explanation with policy references. Task ExplainAsync( ActionPolicyDecision decision, CancellationToken cancellationToken); /// /// Checks if an action has already been executed (idempotency check). /// /// The action proposal. /// The execution context. /// Cancellation token. /// True if the action was already executed with the same parameters. Task CheckIdempotencyAsync( ActionProposal proposal, ActionContext context, CancellationToken cancellationToken); } /// /// Context for action policy evaluation. /// public sealed record ActionContext { /// /// Tenant identifier for multi-tenancy. /// public required string TenantId { get; init; } /// /// User identifier who initiated the action. /// public required string UserId { get; init; } /// /// User's roles/permissions. /// public required ImmutableArray UserRoles { get; init; } /// /// Target environment (production, staging, development, etc.). /// public required string Environment { get; init; } /// /// Associated AI run ID, if any. /// public string? RunId { get; init; } /// /// Associated finding ID for remediation actions. /// public string? FindingId { get; init; } /// /// CVE ID if this is a vulnerability-related action. /// public string? CveId { get; init; } /// /// Image digest if this is a container-related action. /// public string? ImageDigest { get; init; } /// /// Optional correlation ID for tracing. /// public string? CorrelationId { get; init; } /// /// Additional metadata for policy evaluation. /// public ImmutableDictionary Metadata { get; init; } = ImmutableDictionary.Empty; } /// /// An action proposed by the AI system. /// public sealed record ActionProposal { /// /// Unique identifier for this proposal. /// public required string ProposalId { get; init; } /// /// Type of action (e.g., "approve", "quarantine", "create_vex"). /// public required string ActionType { get; init; } /// /// Human-readable label for the action. /// public required string Label { get; init; } /// /// Action parameters. /// public required ImmutableDictionary Parameters { get; init; } /// /// When the proposal was created. /// public required DateTimeOffset CreatedAt { get; init; } /// /// When the proposal expires (null = never). /// public DateTimeOffset? ExpiresAt { get; init; } /// /// Idempotency key for deduplication. /// public string? IdempotencyKey { get; init; } } /// /// Result of policy gate evaluation. /// public sealed record ActionPolicyDecision { /// /// The decision outcome. /// public required PolicyDecisionKind Decision { get; init; } /// /// Reference to the policy that made this decision. /// public string? PolicyId { get; init; } /// /// Brief reason for the decision. /// public string? Reason { get; init; } /// /// Required approvers if decision is AllowWithApproval. /// public ImmutableArray RequiredApprovers { get; init; } = ImmutableArray.Empty; /// /// Approval workflow ID if approval is required. /// public string? ApprovalWorkflowId { get; init; } /// /// K4 lattice position used in the decision. /// public string? K4Position { get; init; } /// /// VEX status that influenced the decision, if any. /// public string? VexStatus { get; init; } /// /// Severity level assigned by policy. /// public int? SeverityLevel { get; init; } /// /// When this decision expires. /// public DateTimeOffset? ExpiresAt { get; init; } /// /// Additional decision metadata. /// public ImmutableDictionary Metadata { get; init; } = ImmutableDictionary.Empty; } /// /// Kinds of policy decisions. /// public enum PolicyDecisionKind { /// /// Action is allowed and can execute immediately. /// Allow, /// /// Action is allowed but requires approval workflow. /// AllowWithApproval, /// /// Action is denied by policy. /// Deny, /// /// Action is denied but admin can override. /// DenyWithOverride, /// /// Decision could not be made (missing context). /// Indeterminate } /// /// Describes a required approver for AllowWithApproval decisions. /// public sealed record RequiredApprover { /// /// Type of approver requirement. /// public required ApproverType Type { get; init; } /// /// Identifier (user ID, role name, or group name). /// public required string Identifier { get; init; } /// /// Human-readable description. /// public string? Description { get; init; } } /// /// Types of approval requirements. /// public enum ApproverType { /// /// Specific user must approve. /// User, /// /// Any user with this role can approve. /// Role, /// /// Any member of this group can approve. /// Group } /// /// Human-readable explanation of a policy decision. /// public sealed record PolicyExplanation { /// /// Natural language summary of the decision. /// public required string Summary { get; init; } /// /// Detailed explanation points. /// public required ImmutableArray Details { get; init; } /// /// References to policies that were evaluated. /// public ImmutableArray PolicyReferences { get; init; } = ImmutableArray.Empty; /// /// Suggested next steps for the user. /// public ImmutableArray SuggestedActions { get; init; } = ImmutableArray.Empty; } /// /// Reference to a specific policy. /// public sealed record PolicyReference { /// /// Policy identifier. /// public required string PolicyId { get; init; } /// /// Policy name. /// public required string Name { get; init; } /// /// Rule within the policy that matched. /// public string? RuleId { get; init; } /// /// Link to policy documentation. /// public string? DocumentationUrl { get; init; } } /// /// Result of idempotency check. /// public sealed record IdempotencyCheckResult { /// /// Whether the action was previously executed. /// public required bool WasExecuted { get; init; } /// /// Previous execution ID if executed. /// public string? PreviousExecutionId { get; init; } /// /// When the action was previously executed. /// public DateTimeOffset? ExecutedAt { get; init; } /// /// Result of the previous execution. /// public string? PreviousResult { get; init; } }