sprints completion. new product advisories prepared

This commit is contained in:
master
2026-01-16 16:30:03 +02:00
parent a927d924e3
commit 4ca3ce8fb4
255 changed files with 42434 additions and 1020 deletions

View File

@@ -59,6 +59,16 @@ internal static class ReachabilityEndpoints
.Produces(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status404NotFound)
.RequireAuthorization(ScannerPolicies.ScansRead);
// Sprint: SPRINT_20260112_004_SCANNER_reachability_trace_runtime_evidence
// GET /scans/{scanId}/reachability/traces/export - Trace export with runtime evidence
scansGroup.MapGet("/{scanId}/reachability/traces/export", HandleTraceExportAsync)
.WithName("scanner.scans.reachability.traces.export")
.WithTags("Reachability")
.Produces<ReachabilityTraceExportDto>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status404NotFound)
.RequireAuthorization(ScannerPolicies.ScansRead);
}
private static async Task<IResult> HandleComputeReachabilityAsync(
@@ -315,9 +325,145 @@ internal static class ReachabilityEndpoints
return Json(response, StatusCodes.Status200OK);
}
// Sprint: SPRINT_20260112_004_SCANNER_reachability_trace_runtime_evidence (SCAN-RT-003)
private static async Task<IResult> HandleTraceExportAsync(
string scanId,
string? format,
bool? includeRuntimeEvidence,
double? minReachabilityScore,
bool? runtimeConfirmedOnly,
IScanCoordinator coordinator,
IReachabilityQueryService queryService,
HttpContext context,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(coordinator);
ArgumentNullException.ThrowIfNull(queryService);
if (!ScanId.TryParse(scanId, out var parsed))
{
return ProblemResultFactory.Create(
context,
ProblemTypes.Validation,
"Invalid scan identifier",
StatusCodes.Status400BadRequest,
detail: "Scan identifier is required.");
}
var snapshot = await coordinator.GetAsync(parsed, cancellationToken).ConfigureAwait(false);
if (snapshot is null)
{
return ProblemResultFactory.Create(
context,
ProblemTypes.NotFound,
"Scan not found",
StatusCodes.Status404NotFound,
detail: "Requested scan could not be located.");
}
// Determine export format (default to json-lines for determinism)
var exportFormat = (format?.ToLowerInvariant()) switch
{
"graphson" => "graphson",
"ndjson" or "json-lines" => "json-lines",
_ => "json-lines"
};
var options = new TraceExportOptions
{
Format = exportFormat,
IncludeRuntimeEvidence = includeRuntimeEvidence ?? true,
MinReachabilityScore = minReachabilityScore,
RuntimeConfirmedOnly = runtimeConfirmedOnly ?? false
};
var export = await queryService.ExportTracesAsync(parsed, options, cancellationToken).ConfigureAwait(false);
if (export is null)
{
return ProblemResultFactory.Create(
context,
ProblemTypes.NotFound,
"No reachability data",
StatusCodes.Status404NotFound,
detail: "No reachability data found for this scan.");
}
var response = new ReachabilityTraceExportDto(
Format: export.Format,
CanonicalizationMethod: "StellaOps.Canonical.Json",
ContentDigest: export.ContentDigest,
Timestamp: export.Timestamp,
NodeCount: export.Nodes.Count,
EdgeCount: export.Edges.Count,
RuntimeCoverage: export.RuntimeCoverage,
AverageReachabilityScore: export.AverageReachabilityScore,
Nodes: export.Nodes.Select(n => new TraceNodeDto(
Id: n.Id,
SymbolId: n.SymbolId,
ReachabilityScore: n.ReachabilityScore,
RuntimeConfirmed: n.RuntimeConfirmed,
RuntimeObservationCount: n.RuntimeObservationCount,
Evidence: n.Evidence)).ToList(),
Edges: export.Edges.Select(e => new TraceEdgeDto(
From: e.From,
To: e.To,
Kind: e.Kind,
Confidence: e.Confidence,
RuntimeConfirmed: e.RuntimeConfirmed,
RuntimeObservationCount: e.RuntimeObservationCount,
Evidence: e.Evidence)).ToList());
return Json(response, StatusCodes.Status200OK);
}
private static IResult Json<T>(T value, int statusCode)
{
var payload = JsonSerializer.Serialize(value, SerializerOptions);
return Results.Content(payload, "application/json", System.Text.Encoding.UTF8, statusCode);
}
}
// Sprint: SPRINT_20260112_004_SCANNER_reachability_trace_runtime_evidence
// Trace export DTOs
/// <summary>Options for trace export.</summary>
public sealed record TraceExportOptions
{
public string Format { get; init; } = "json-lines";
public bool IncludeRuntimeEvidence { get; init; } = true;
public double? MinReachabilityScore { get; init; }
public bool RuntimeConfirmedOnly { get; init; }
}
/// <summary>Trace export response.</summary>
public sealed record ReachabilityTraceExportDto(
string Format,
string CanonicalizationMethod,
string ContentDigest,
DateTimeOffset Timestamp,
int NodeCount,
int EdgeCount,
double RuntimeCoverage,
double? AverageReachabilityScore,
IReadOnlyList<TraceNodeDto> Nodes,
IReadOnlyList<TraceEdgeDto> Edges);
/// <summary>Node in trace export.</summary>
public sealed record TraceNodeDto(
string Id,
string SymbolId,
double? ReachabilityScore,
bool? RuntimeConfirmed,
ulong? RuntimeObservationCount,
IReadOnlyList<string>? Evidence);
/// <summary>Edge in trace export.</summary>
public sealed record TraceEdgeDto(
string From,
string To,
string Kind,
double Confidence,
bool? RuntimeConfirmed,
ulong? RuntimeObservationCount,
IReadOnlyList<string>? Evidence);