Compliance & Reporting

Overview

Compliance & Reporting transforms the Release Orchestrator’s audit capabilities into a comprehensive compliance management system. This enhancement provides pre-built compliance report templates, evidence chain visualization, audit query interface, regulatory framework alignment, and automated compliance checking.

This is a best-in-class implementation designed to meet the needs of enterprises operating under strict regulatory requirements (SOC2, ISO 27001, PCI-DSS, HIPAA, FedRAMP, GDPR).


Design Principles

  1. Continuous Compliance: Real-time compliance status, not periodic audits
  2. Evidence-First: All compliance claims backed by cryptographic evidence
  3. Framework-Agnostic: Adaptable to any regulatory framework
  4. Auditor-Friendly: Reports designed for external auditor consumption
  5. Immutable Records: Tamper-proof audit trail
  6. Automated Where Possible: Reduce manual compliance burden

Runtime truthfulness rule: automated compliance must fail closed when a control has no evidence-backed validator. Unsupported automated controls are failed controls with remediation guidance, not not applicable; manual review controls may remain partial until a reviewer disposition is recorded.


Architecture

Component Overview

┌────────────────────────────────────────────────────────────────────────┐
│                   Compliance & Reporting System                        │
├────────────────────────────────────────────────────────────────────────┤
│                                                                        │
│  ┌──────────────────┐    ┌───────────────────┐    ┌─────────────────┐ │
│  │ ComplianceEngine │───▶│ ReportGenerator   │───▶│ EvidenceChain   │ │
│  │                  │    │                   │    │ Visualizer      │ │
│  └──────────────────┘    └───────────────────┘    └─────────────────┘ │
│           │                       │                        │          │
│           ▼                       ▼                        ▼          │
│  ┌──────────────────┐    ┌───────────────────┐    ┌─────────────────┐ │
│  │ FrameworkMapper  │    │ AuditQueryEngine  │    │ ControlValidator│ │
│  │                  │    │                   │    │                 │ │
│  └──────────────────┘    └───────────────────┘    └─────────────────┘ │
│           │                       │                        │          │
│           ▼                       ▼                        ▼          │
│  ┌──────────────────┐    ┌───────────────────┐    ┌─────────────────┐ │
│  │ ExportService    │    │ ScheduledReports  │    │ AlertManager    │ │
│  │                  │    │                   │    │                 │ │
│  └──────────────────┘    └───────────────────┘    └─────────────────┘ │
│                                                                        │
└────────────────────────────────────────────────────────────────────────┘

Key Components

1. ComplianceEngine

Core compliance evaluation engine:

public sealed class ComplianceEngine
{
    private readonly ImmutableArray<IComplianceFramework> _frameworks;
    private readonly IControlValidator _validator;
    private readonly IEvidenceStore _evidenceStore;

    public async Task<ComplianceStatus> EvaluateAsync(
        ComplianceEvaluationRequest request,
        CancellationToken ct)
    {
        var status = new ComplianceStatus
        {
            TenantId = request.TenantId,
            EvaluatedAt = _timeProvider.GetUtcNow(),
            Frameworks = new List<FrameworkStatus>()
        };

        foreach (var frameworkId in request.Frameworks)
        {
            var framework = _frameworks.First(f => f.Id == frameworkId);
            var frameworkStatus = await EvaluateFrameworkAsync(framework, request, ct);
            status.Frameworks.Add(frameworkStatus);
        }

        // Calculate overall compliance score
        status.OverallScore = CalculateOverallScore(status.Frameworks);
        status.ComplianceLevel = DetermineComplianceLevel(status.OverallScore);

        return status;
    }

    private async Task<FrameworkStatus> EvaluateFrameworkAsync(
        IComplianceFramework framework,
        ComplianceEvaluationRequest request,
        CancellationToken ct)
    {
        var frameworkStatus = new FrameworkStatus
        {
            FrameworkId = framework.Id,
            FrameworkName = framework.Name,
            Version = framework.Version,
            Controls = new List<ControlStatus>()
        };

        foreach (var control in framework.Controls)
        {
            var controlStatus = await EvaluateControlAsync(control, request, ct);
            frameworkStatus.Controls.Add(controlStatus);
        }

        // Calculate framework compliance
        frameworkStatus.TotalControls = framework.Controls.Count;
        frameworkStatus.PassedControls = frameworkStatus.Controls.Count(c => c.Status == ControlEvaluationStatus.Passed);
        frameworkStatus.FailedControls = frameworkStatus.Controls.Count(c => c.Status == ControlEvaluationStatus.Failed);
        frameworkStatus.NotApplicableControls = frameworkStatus.Controls.Count(c => c.Status == ControlEvaluationStatus.NotApplicable);
        frameworkStatus.Score = (double)frameworkStatus.PassedControls /
            (frameworkStatus.TotalControls - frameworkStatus.NotApplicableControls) * 100;

        return frameworkStatus;
    }

    private async Task<ControlStatus> EvaluateControlAsync(
        ComplianceControl control,
        ComplianceEvaluationRequest request,
        CancellationToken ct)
    {
        var controlStatus = new ControlStatus
        {
            ControlId = control.Id,
            ControlName = control.Name,
            Category = control.Category,
            Description = control.Description,
            Evidence = new List<EvidenceReference>()
        };

        // Validate control
        var validationResult = await _validator.ValidateAsync(control, request, ct);
        controlStatus.Status = validationResult.Status;
        controlStatus.Findings = validationResult.Findings;

        // Collect evidence
        var evidence = await _evidenceStore.GetEvidenceForControlAsync(
            request.TenantId, control.Id, request.DateRange, ct);
        controlStatus.Evidence = evidence.Select(e => new EvidenceReference
        {
            EvidenceId = e.Id,
            Type = e.Type,
            CollectedAt = e.CollectedAt,
            Summary = e.Summary
        }).ToList();

        return controlStatus;
    }
}

public sealed record ComplianceStatus
{
    public Guid TenantId { get; init; }
    public DateTimeOffset EvaluatedAt { get; init; }
    public double OverallScore { get; init; }
    public ComplianceLevel ComplianceLevel { get; init; }
    public List<FrameworkStatus> Frameworks { get; init; }
}

public enum ComplianceLevel
{
    FullyCompliant,      // 100%
    SubstantiallyCompliant, // 90-99%
    PartiallyCompliant,  // 70-89%
    NonCompliant         // <70%
}

2. FrameworkMapper

Maps organizational controls to compliance frameworks:

public sealed class FrameworkMapper
{
    private readonly ImmutableDictionary<string, IComplianceFramework> _frameworks;

    public FrameworkMapper()
    {
        _frameworks = LoadFrameworks().ToImmutableDictionary(f => f.Id);
    }

    private IEnumerable<IComplianceFramework> LoadFrameworks()
    {
        yield return new Soc2Framework();
        yield return new Iso27001Framework();
        yield return new PciDssFramework();
        yield return new HipaaFramework();
        yield return new FedRampFramework();
        yield return new GdprFramework();
        yield return new NistCsfFramework();
    }

    public IReadOnlyList<ControlMapping> MapToFramework(
        string frameworkId,
        IReadOnlyList<OrganizationalControl> orgControls)
    {
        var framework = _frameworks[frameworkId];
        var mappings = new List<ControlMapping>();

        foreach (var frameworkControl in framework.Controls)
        {
            var mapping = new ControlMapping
            {
                FrameworkControl = frameworkControl,
                MappedOrgControls = new List<OrganizationalControl>()
            };

            // Find matching organizational controls
            foreach (var orgControl in orgControls)
            {
                if (IsMatch(frameworkControl, orgControl))
                {
                    mapping.MappedOrgControls.Add(orgControl);
                }
            }

            mapping.CoverageStatus = mapping.MappedOrgControls.Any()
                ? CoverageStatus.Covered
                : CoverageStatus.Gap;

            mappings.Add(mapping);
        }

        return mappings;
    }

    private bool IsMatch(ComplianceControl frameworkControl, OrganizationalControl orgControl)
    {
        // Check explicit mappings
        if (orgControl.FrameworkMappings?.Contains(frameworkControl.Id) == true)
            return true;

        // Check keyword matching
        var keywords = frameworkControl.Keywords ?? ImmutableArray<string>.Empty;
        return keywords.Any(k => orgControl.Description?.Contains(k, StringComparison.OrdinalIgnoreCase) == true);
    }
}

// SOC 2 Framework Implementation
public sealed class Soc2Framework : IComplianceFramework
{
    public string Id => "soc2-type2";
    public string Name => "SOC 2 Type II";
    public string Version => "2017";

    public ImmutableArray<ComplianceControl> Controls => new[]
    {
        // Security (Common Criteria)
        new ComplianceControl
        {
            Id = "CC1.1",
            Name = "COSO Principle 1",
            Category = "Control Environment",
            Description = "The entity demonstrates a commitment to integrity and ethical values.",
            Keywords = new[] { "integrity", "ethics", "code of conduct" }.ToImmutableArray()
        },
        new ComplianceControl
        {
            Id = "CC6.1",
            Name = "Logical and Physical Access Controls",
            Category = "Logical and Physical Access",
            Description = "The entity implements logical access security software, infrastructure, and architectures.",
            Keywords = new[] { "access control", "authentication", "authorization", "mTLS" }.ToImmutableArray(),
            AutomatedChecks = new[]
            {
                new AutomatedCheck
                {
                    Id = "cc6.1.1",
                    Description = "All agent connections use mTLS",
                    CheckType = CheckType.AgentSecurity
                },
                new AutomatedCheck
                {
                    Id = "cc6.1.2",
                    Description = "User authentication via SSO/OIDC",
                    CheckType = CheckType.AuthenticationMethod
                }
            }.ToImmutableArray()
        },
        new ComplianceControl
        {
            Id = "CC7.2",
            Name = "System Operations",
            Category = "System Operations",
            Description = "The entity monitors system components and the operation of those components for anomalies.",
            Keywords = new[] { "monitoring", "alerting", "anomaly detection" }.ToImmutableArray()
        },
        new ComplianceControl
        {
            Id = "CC8.1",
            Name = "Change Management",
            Category = "Change Management",
            Description = "The entity authorizes, designs, develops, configures, documents, tests, approves, and implements changes.",
            Keywords = new[] { "change management", "approval", "deployment", "release" }.ToImmutableArray(),
            AutomatedChecks = new[]
            {
                new AutomatedCheck
                {
                    Id = "cc8.1.1",
                    Description = "All production deployments require approval",
                    CheckType = CheckType.ApprovalRequired
                },
                new AutomatedCheck
                {
                    Id = "cc8.1.2",
                    Description = "All changes produce evidence packets",
                    CheckType = CheckType.EvidenceGenerated
                }
            }.ToImmutableArray()
        }
        // ... more controls
    }.ToImmutableArray();
}

3. ReportGenerator

Generates compliance reports:

public sealed class ReportGenerator
{
    public async Task<ComplianceReport> GenerateAsync(
        ReportRequest request,
        CancellationToken ct)
    {
        var report = new ComplianceReport
        {
            Id = Guid.NewGuid(),
            Type = request.ReportType,
            GeneratedAt = _timeProvider.GetUtcNow(),
            GeneratedBy = request.RequestedBy,
            DateRange = request.DateRange
        };

        // Get compliance status
        var status = await _complianceEngine.EvaluateAsync(new ComplianceEvaluationRequest
        {
            TenantId = request.TenantId,
            Frameworks = request.Frameworks,
            DateRange = request.DateRange
        }, ct);

        report.ComplianceStatus = status;

        // Generate sections based on report type
        switch (request.ReportType)
        {
            case ReportType.ExecutiveSummary:
                report.Sections = await GenerateExecutiveSummaryAsync(status, ct);
                break;

            case ReportType.DetailedCompliance:
                report.Sections = await GenerateDetailedReportAsync(status, request, ct);
                break;

            case ReportType.GapAnalysis:
                report.Sections = await GenerateGapAnalysisAsync(status, ct);
                break;

            case ReportType.AuditReadiness:
                report.Sections = await GenerateAuditReadinessAsync(status, request, ct);
                break;

            case ReportType.EvidencePackage:
                report.Sections = await GenerateEvidencePackageAsync(status, request, ct);
                break;
        }

        // Add standard sections
        report.Sections.Add(GenerateMethodologySection());
        report.Sections.Add(GenerateDisclaimerSection());

        return report;
    }

    private async Task<List<ReportSection>> GenerateDetailedReportAsync(
        ComplianceStatus status,
        ReportRequest request,
        CancellationToken ct)
    {
        var sections = new List<ReportSection>();

        // Overview section
        sections.Add(new ReportSection
        {
            Title = "Compliance Overview",
            Content = new OverviewContent
            {
                EvaluationDate = status.EvaluatedAt,
                OverallScore = status.OverallScore,
                ComplianceLevel = status.ComplianceLevel,
                FrameworkSummaries = status.Frameworks.Select(f => new FrameworkSummary
                {
                    Name = f.FrameworkName,
                    Score = f.Score,
                    PassedControls = f.PassedControls,
                    TotalControls = f.TotalControls
                }).ToList()
            }
        });

        // Per-framework sections
        foreach (var framework in status.Frameworks)
        {
            var frameworkSection = new ReportSection
            {
                Title = $"{framework.FrameworkName} Compliance",
                Subsections = new List<ReportSection>()
            };

            // Group controls by category
            var byCategory = framework.Controls.GroupBy(c => c.Category);
            foreach (var category in byCategory)
            {
                var categorySection = new ReportSection
                {
                    Title = category.Key,
                    Content = new ControlCategoryContent
                    {
                        Controls = category.Select(c => new ControlDetail
                        {
                            Id = c.ControlId,
                            Name = c.ControlName,
                            Status = c.Status,
                            Findings = c.Findings,
                            EvidenceCount = c.Evidence.Count,
                            EvidenceReferences = c.Evidence
                        }).ToList()
                    }
                };
                frameworkSection.Subsections.Add(categorySection);
            }

            sections.Add(frameworkSection);
        }

        // Findings summary
        var allFindings = status.Frameworks
            .SelectMany(f => f.Controls)
            .SelectMany(c => c.Findings ?? Enumerable.Empty<Finding>())
            .ToList();

        sections.Add(new ReportSection
        {
            Title = "Findings Summary",
            Content = new FindingsSummaryContent
            {
                TotalFindings = allFindings.Count,
                CriticalFindings = allFindings.Count(f => f.Severity == FindingSeverity.Critical),
                HighFindings = allFindings.Count(f => f.Severity == FindingSeverity.High),
                MediumFindings = allFindings.Count(f => f.Severity == FindingSeverity.Medium),
                LowFindings = allFindings.Count(f => f.Severity == FindingSeverity.Low),
                Findings = allFindings.OrderByDescending(f => f.Severity).ToList()
            }
        });

        // Recommendations
        sections.Add(await GenerateRecommendationsAsync(status, ct));

        return sections;
    }
}

4. EvidenceChainVisualizer

Visualizes evidence chains:

public sealed class EvidenceChainVisualizer
{
    public async Task<EvidenceChainVisualization> VisualizeAsync(
        Guid rootEvidenceId,
        CancellationToken ct)
    {
        var root = await _evidenceStore.GetAsync(rootEvidenceId, ct);
        var visualization = new EvidenceChainVisualization
        {
            RootEvidenceId = rootEvidenceId,
            GeneratedAt = _timeProvider.GetUtcNow()
        };

        // Build the chain
        var chain = await BuildChainAsync(root, ct);
        visualization.Chain = chain;

        // Create graph representation
        visualization.Graph = CreateGraph(chain);

        // Verify chain integrity
        visualization.IntegrityVerification = await VerifyChainIntegrityAsync(chain, ct);

        // Generate narrative
        visualization.Narrative = GenerateNarrative(chain);

        return visualization;
    }

    private async Task<EvidenceChain> BuildChainAsync(
        EvidencePacket root,
        CancellationToken ct)
    {
        var chain = new EvidenceChain
        {
            Nodes = new List<EvidenceNode>(),
            Edges = new List<EvidenceEdge>()
        };

        var visited = new HashSet<Guid>();
        var queue = new Queue<EvidencePacket>();
        queue.Enqueue(root);

        while (queue.Count > 0)
        {
            var current = queue.Dequeue();
            if (visited.Contains(current.Id))
                continue;

            visited.Add(current.Id);

            // Add node
            chain.Nodes.Add(new EvidenceNode
            {
                Id = current.Id,
                Type = current.SubjectType,
                Subject = current.SubjectId,
                CollectedAt = current.CollectedAt,
                Summary = GenerateSummary(current),
                Signature = current.Signature,
                SignatureValid = await VerifySignatureAsync(current, ct)
            });

            // Add edges for dependencies
            foreach (var depId in current.DependsOn)
            {
                chain.Edges.Add(new EvidenceEdge
                {
                    FromId = depId,
                    ToId = current.Id,
                    Relationship = "depends_on"
                });

                // Load dependent evidence
                var dep = await _evidenceStore.GetAsync(depId, ct);
                if (dep != null && !visited.Contains(dep.Id))
                {
                    queue.Enqueue(dep);
                }
            }
        }

        return chain;
    }

    private EvidenceGraph CreateGraph(EvidenceChain chain)
    {
        var graph = new EvidenceGraph();

        // Calculate layout (topological sort + horizontal levels)
        var levels = CalculateLevels(chain);

        foreach (var (level, nodes) in levels)
        {
            var y = level * 100;
            var x = 0;
            foreach (var node in nodes)
            {
                graph.Nodes.Add(new GraphNode
                {
                    Id = node.Id.ToString(),
                    Label = $"{node.Type}\n{node.CollectedAt:g}",
                    X = x,
                    Y = y,
                    Color = GetNodeColor(node)
                });
                x += 150;
            }
        }

        foreach (var edge in chain.Edges)
        {
            graph.Edges.Add(new GraphEdge
            {
                From = edge.FromId.ToString(),
                To = edge.ToId.ToString(),
                Label = edge.Relationship
            });
        }

        return graph;
    }

    private string GenerateNarrative(EvidenceChain chain)
    {
        var sb = new StringBuilder();
        var ordered = chain.Nodes.OrderBy(n => n.CollectedAt).ToList();

        sb.AppendLine("## Evidence Chain Narrative");
        sb.AppendLine();

        foreach (var node in ordered)
        {
            sb.AppendLine($"### {node.CollectedAt:yyyy-MM-dd HH:mm:ss} UTC");
            sb.AppendLine();
            sb.AppendLine($"**{node.Type}** (ID: `{node.Id}`)");
            sb.AppendLine();
            sb.AppendLine(node.Summary);
            sb.AppendLine();

            if (node.SignatureValid)
            {
                sb.AppendLine($"✓ Signature verified");
            }
            else
            {
                sb.AppendLine($"⚠ Signature verification failed");
            }
            sb.AppendLine();
        }

        return sb.ToString();
    }
}

5. AuditQueryEngine

Powerful query interface for audit data:

public sealed class AuditQueryEngine
{
    public async Task<AuditQueryResult> QueryAsync(
        AuditQuery query,
        CancellationToken ct)
    {
        var result = new AuditQueryResult
        {
            QueryId = Guid.NewGuid(),
            ExecutedAt = _timeProvider.GetUtcNow(),
            Query = query
        };

        // Build SQL from query
        var sql = BuildQuery(query);

        // Execute
        var connection = await _connectionPool.GetReadReplicaAsync(ct);
        var records = await connection.QueryAsync<AuditRecord>(sql.ToString(), query.Parameters, ct);

        result.Records = records.ToImmutableArray();
        result.TotalCount = records.Count();

        // Apply aggregations if requested
        if (query.Aggregations != null)
        {
            result.Aggregations = ApplyAggregations(records, query.Aggregations);
        }

        return result;
    }

    private string BuildQuery(AuditQuery query)
    {
        var sql = new StringBuilder();

        // Base query
        sql.AppendLine(@"
            SELECT
                e.id,
                e.subject_type,
                e.subject_id,
                e.collected_at,
                e.content,
                e.signature,
                u.email as actor_email,
                u.name as actor_name
            FROM evidence_packets e
            LEFT JOIN users u ON e.actor_id = u.id
            WHERE e.tenant_id = @TenantId");

        // Date range
        if (query.DateRange != null)
        {
            sql.AppendLine("AND e.collected_at >= @StartDate");
            sql.AppendLine("AND e.collected_at <= @EndDate");
        }

        // Subject type filter
        if (query.SubjectTypes?.Any() == true)
        {
            sql.AppendLine("AND e.subject_type = ANY(@SubjectTypes)");
        }

        // Actor filter
        if (query.ActorId.HasValue)
        {
            sql.AppendLine("AND e.actor_id = @ActorId");
        }

        // Text search
        if (!string.IsNullOrEmpty(query.SearchText))
        {
            sql.AppendLine("AND e.content_tsv @@ plainto_tsquery(@SearchText)");
        }

        // Custom filters
        foreach (var filter in query.Filters ?? Enumerable.Empty<QueryFilter>())
        {
            sql.AppendLine($"AND {BuildFilterClause(filter)}");
        }

        // Ordering
        sql.AppendLine("ORDER BY e.collected_at DESC");

        // Pagination
        if (query.Limit.HasValue)
        {
            sql.AppendLine($"LIMIT {query.Limit}");
        }
        if (query.Offset.HasValue)
        {
            sql.AppendLine($"OFFSET {query.Offset}");
        }

        return sql.ToString();
    }
}

public sealed record AuditQuery
{
    public Guid TenantId { get; init; }
    public DateRange? DateRange { get; init; }
    public ImmutableArray<string>? SubjectTypes { get; init; }
    public Guid? ActorId { get; init; }
    public string? SearchText { get; init; }
    public ImmutableArray<QueryFilter>? Filters { get; init; }
    public ImmutableArray<string>? Aggregations { get; init; }
    public int? Limit { get; init; }
    public int? Offset { get; init; }
}

6. ControlValidator

Automated control validation:

public sealed class ControlValidator : IControlValidator
{
    private readonly ImmutableDictionary<CheckType, IAutomatedCheck> _checks;

    public async Task<ControlValidationResult> ValidateAsync(
        ComplianceControl control,
        ComplianceEvaluationRequest request,
        CancellationToken ct)
    {
        var result = new ControlValidationResult
        {
            ControlId = control.Id,
            Findings = new List<Finding>()
        };

        // Run automated checks
        if (control.AutomatedChecks?.Any() == true)
        {
            foreach (var check in control.AutomatedChecks)
            {
                var checkImpl = _checks.GetValueOrDefault(check.CheckType);
                if (checkImpl == null)
                {
                    result.Findings.Add(new Finding
                    {
                        Severity = FindingSeverity.Low,
                        Message = $"Automated check {check.Id} not implemented",
                        CheckId = check.Id
                    });
                    continue;
                }

                var checkResult = await checkImpl.ExecuteAsync(request, ct);
                if (!checkResult.Passed)
                {
                    result.Findings.Add(new Finding
                    {
                        Severity = checkResult.Severity,
                        Message = checkResult.Message,
                        CheckId = check.Id,
                        Details = checkResult.Details
                    });
                }
            }
        }

        // Determine overall status
        if (result.Findings.Any(f => f.Severity >= FindingSeverity.High))
        {
            result.Status = ControlEvaluationStatus.Failed;
        }
        else if (result.Findings.Any())
        {
            result.Status = ControlEvaluationStatus.PartiallyMet;
        }
        else
        {
            result.Status = ControlEvaluationStatus.Passed;
        }

        return result;
    }
}

// Example automated check implementations
public sealed class ApprovalRequiredCheck : IAutomatedCheck
{
    public CheckType Type => CheckType.ApprovalRequired;

    public async Task<CheckResult> ExecuteAsync(
        ComplianceEvaluationRequest request,
        CancellationToken ct)
    {
        // Check that all production deployments required approval
        var deployments = await _deploymentStore.GetByDateRangeAsync(
            request.TenantId, request.DateRange, ct);

        var productionDeployments = deployments
            .Where(d => d.Environment.Name.Equals("production", StringComparison.OrdinalIgnoreCase));

        var withoutApproval = productionDeployments
            .Where(d => d.ApprovalRecords?.Any() != true)
            .ToList();

        if (withoutApproval.Any())
        {
            return new CheckResult
            {
                Passed = false,
                Severity = FindingSeverity.Critical,
                Message = $"{withoutApproval.Count} production deployments without approval",
                Details = withoutApproval.Select(d => new
                {
                    d.Id,
                    d.ReleaseId,
                    d.DeployedAt
                }).ToList()
            };
        }

        return CheckResult.Pass();
    }
}

public sealed class EvidenceGeneratedCheck : IAutomatedCheck
{
    public CheckType Type => CheckType.EvidenceGenerated;

    public async Task<CheckResult> ExecuteAsync(
        ComplianceEvaluationRequest request,
        CancellationToken ct)
    {
        // Check that all deployments generated evidence
        var deployments = await _deploymentStore.GetByDateRangeAsync(
            request.TenantId, request.DateRange, ct);

        var withoutEvidence = new List<Deployment>();
        foreach (var deployment in deployments)
        {
            var evidence = await _evidenceStore.GetBySubjectAsync(
                "deployment", deployment.Id, ct);

            if (evidence == null)
            {
                withoutEvidence.Add(deployment);
            }
        }

        if (withoutEvidence.Any())
        {
            return new CheckResult
            {
                Passed = false,
                Severity = FindingSeverity.High,
                Message = $"{withoutEvidence.Count} deployments without evidence packets",
                Details = withoutEvidence.Select(d => d.Id).ToList()
            };
        }

        return CheckResult.Pass();
    }
}

Report Templates

Executive Summary Template

# Compliance Executive Summary

**Organization:** {{organization.name}}
**Report Period:** {{date_range.start}} to {{date_range.end}}
**Generated:** {{generated_at}}

## Overall Compliance Status

| Framework | Score | Status |
|-----------|-------|--------|
{{#each frameworks}}
| {{name}} | {{score}}% | {{status}} |
{{/each}}

**Overall Compliance Level:** {{compliance_level}}

## Key Findings

{{#if critical_findings}}
### Critical Issues ({{critical_findings.count}})
{{#each critical_findings}}
- **{{control_id}}**: {{message}}
{{/each}}
{{/if}}

{{#if high_findings}}
### High Priority Issues ({{high_findings.count}})
{{#each high_findings}}
- **{{control_id}}**: {{message}}
{{/each}}
{{/if}}

## Recommendations

{{#each recommendations}}
1. **{{title}}** (Priority: {{priority}})
   {{description}}
{{/each}}

## Next Steps

1. Address critical findings within {{sla.critical}} days
2. Review and remediate high-priority findings
3. Schedule follow-up assessment for {{next_assessment_date}}

Audit Readiness Report

# Audit Readiness Report

**Framework:** {{framework.name}} {{framework.version}}
**Assessment Date:** {{generated_at}}

## Readiness Summary

**Ready for Audit:** {{#if ready}}Yes{{else}}No{{/if}}
**Controls Passing:** {{passing_controls}} / {{total_controls}}
**Evidence Coverage:** {{evidence_coverage}}%

## Control-by-Control Assessment

{{#each control_categories}}
### {{category_name}}

{{#each controls}}
#### {{control_id}} - {{control_name}}

**Status:** {{status}}
**Evidence Available:** {{evidence_count}} items

{{#if findings}}
**Findings:**
{{#each findings}}
- [{{severity}}] {{message}}
{{/each}}
{{/if}}

{{#if evidence}}
**Evidence Summary:**
{{#each evidence}}
- {{type}} ({{collected_at}}): {{summary}}
{{/each}}
{{/if}}

---
{{/each}}
{{/each}}

## Gap Analysis

{{#each gaps}}
| Control | Gap Description | Remediation Recommendation |
|---------|-----------------|---------------------------|
{{#each items}}
| {{control_id}} | {{gap}} | {{recommendation}} |
{{/each}}
{{/each}}

## Evidence Package Checklist

{{#each evidence_checklist}}
- [{{#if available}}x{{else}} {{/if}}] {{item}}
{{/each}}

API Design

REST Endpoints

# Compliance Status
GET    /api/v1/compliance/status                  # Current compliance status
GET    /api/v1/compliance/status/history          # Historical compliance

# Reports
POST   /api/v1/compliance/reports                 # Generate report
GET    /api/v1/compliance/reports                 # List reports
GET    /api/v1/compliance/reports/{id}            # Get report
GET    /api/v1/compliance/reports/{id}/download   # Download report (PDF/HTML)

# Evidence
GET    /api/v1/compliance/evidence                # List evidence
GET    /api/v1/compliance/evidence/{id}           # Get evidence
GET    /api/v1/compliance/evidence/{id}/chain     # Get evidence chain
GET    /api/v1/compliance/evidence/{id}/verify    # Verify evidence integrity

# Audit Query
POST   /api/v1/compliance/audit/query             # Execute audit query
GET    /api/v1/compliance/audit/saved-queries     # List saved queries
POST   /api/v1/compliance/audit/saved-queries     # Save query

# Frameworks
GET    /api/v1/compliance/frameworks              # List frameworks
GET    /api/v1/compliance/frameworks/{id}         # Get framework details
GET    /api/v1/compliance/frameworks/{id}/controls # Get controls

# Control Mappings
GET    /api/v1/compliance/mappings                # Get control mappings
PUT    /api/v1/compliance/mappings                # Update mappings

# Scheduled Reports
POST   /api/v1/compliance/reports/schedules       # Create schedule
GET    /api/v1/compliance/reports/schedules       # List schedules
DELETE /api/v1/compliance/reports/schedules/{id}  # Delete schedule

Metrics & Observability

Prometheus Metrics

# Compliance Scores
stella_compliance_score{framework, tenant_id}
stella_compliance_controls_passed{framework, tenant_id}
stella_compliance_controls_failed{framework, tenant_id}

# Findings
stella_compliance_findings_total{severity, framework}
stella_compliance_findings_open{severity, framework}
stella_compliance_findings_remediated{severity, framework}

# Evidence
stella_evidence_collected_total{type}
stella_evidence_verification_total{status}
stella_evidence_chain_depth{type}

# Reports
stella_reports_generated_total{type, framework}
stella_report_generation_duration_seconds{type}

# Audit Queries
stella_audit_queries_total{status}
stella_audit_query_duration_seconds

Configuration

compliance:
  frameworks:
    - id: soc2-type2
      enabled: true
      controls_file: "./frameworks/soc2.yaml"

    - id: iso27001
      enabled: true
      controls_file: "./frameworks/iso27001.yaml"

  automated_checks:
    enabled: true
    schedule: "0 0 * * *"  # Daily at midnight

  reports:
    scheduled:
      - name: "Weekly Executive Summary"
        type: executive_summary
        schedule: "0 8 * * 1"  # Monday 8am
        recipients:
          - compliance@example.com
          - ciso@example.com
        format: pdf

      - name: "Monthly Detailed Report"
        type: detailed_compliance
        schedule: "0 8 1 * *"  # 1st of month
        recipients:
          - compliance@example.com
        format: html

  evidence:
    retention_days: 2555  # 7 years
    verification_schedule: "0 */6 * * *"  # Every 6 hours

  alerts:
    compliance_drop_threshold: 90
    critical_finding_channels:
      - type: slack
        channel: "#compliance-alerts"
      - type: email
        recipients:
          - compliance@example.com

Test Strategy

Unit Tests

Integration Tests

Compliance Tests


Migration Path

Phase 1: Framework Foundation (Week 1-2)

Phase 2: Automated Checks (Week 3-4)

Phase 3: Reporting (Week 5-6)

Phase 4: Evidence Chain (Week 7-8)

Phase 5: Audit Query (Week 9-10)

Phase 6: Polish (Week 11-12)