//
// 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; }
}