mroe completeness

This commit is contained in:
StellaOps Bot
2025-12-18 19:24:04 +02:00
parent 7d5250238c
commit 1fcf550d3a
14 changed files with 856 additions and 11 deletions

View File

@@ -117,6 +117,7 @@ builder.Services.AddSingleton<ICallgraphParser>(new SimpleJsonCallgraphParser("p
builder.Services.AddSingleton<ICallgraphParser>(new SimpleJsonCallgraphParser("go"));
builder.Services.AddSingleton<ICallgraphParserResolver, CallgraphParserResolver>();
builder.Services.AddSingleton<ICallgraphIngestionService, CallgraphIngestionService>();
builder.Services.AddSingleton<ICallGraphSyncService, NullCallGraphSyncService>();
builder.Services.AddSingleton<IReachabilityCache>(sp =>
{
var options = sp.GetRequiredService<IOptions<SignalsOptions>>().Value;

View File

@@ -32,6 +32,7 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
private readonly ICallgraphRepository repository;
private readonly IReachabilityStoreRepository reachabilityStore;
private readonly ICallgraphNormalizationService normalizer;
private readonly ICallGraphSyncService syncService;
private readonly ILogger<CallgraphIngestionService> logger;
private readonly SignalsOptions options;
private readonly TimeProvider timeProvider;
@@ -43,6 +44,7 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
ICallgraphRepository repository,
IReachabilityStoreRepository reachabilityStore,
ICallgraphNormalizationService normalizer,
ICallGraphSyncService syncService,
IOptions<SignalsOptions> options,
TimeProvider timeProvider,
ILogger<CallgraphIngestionService> logger)
@@ -52,6 +54,7 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
this.reachabilityStore = reachabilityStore ?? throw new ArgumentNullException(nameof(reachabilityStore));
this.normalizer = normalizer ?? throw new ArgumentNullException(nameof(normalizer));
this.syncService = syncService ?? throw new ArgumentNullException(nameof(syncService));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
this.options = options?.Value ?? throw new ArgumentNullException(nameof(options));
@@ -161,6 +164,8 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
document.Edges,
cancellationToken).ConfigureAwait(false);
await TrySyncRelationalProjectionAsync(document, cancellationToken).ConfigureAwait(false);
logger.LogInformation(
"Ingested callgraph {Language}:{Component}:{Version} (id={Id}) with {NodeCount} nodes and {EdgeCount} edges.",
document.Language,
@@ -183,6 +188,45 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
document.Roots?.Count ?? 0);
}
private async Task TrySyncRelationalProjectionAsync(CallgraphDocument document, CancellationToken cancellationToken)
{
try
{
var syncRequest = new CallGraphSyncRequest(
ArtifactDigest: document.Artifact.Hash,
SbomDigest: GetMetadataValue(document.Metadata, "sbomDigest"),
RepoUri: GetMetadataValue(document.Metadata, "repoUri"),
CommitSha: GetMetadataValue(document.Metadata, "commitSha"),
PolicyDigest: GetMetadataValue(document.Metadata, "policyDigest"),
Document: document);
var result = await syncService.SyncAsync(syncRequest, cancellationToken).ConfigureAwait(false);
if (result.WasApplied)
{
logger.LogInformation("Projected callgraph {CallgraphId} into relational tables (scanId={ScanId}).", document.Id, result.ScanId);
}
}
catch (Exception ex)
{
logger.LogWarning(ex, "Callgraph projection failed for callgraph {CallgraphId}.", document.Id);
}
}
private static string? GetMetadataValue(IReadOnlyDictionary<string, string?>? metadata, string key)
{
if (metadata is null || string.IsNullOrWhiteSpace(key))
{
return null;
}
if (!metadata.TryGetValue(key, out var value))
{
return null;
}
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
private static void ValidateRequest(CallgraphIngestRequest request)
{
ArgumentNullException.ThrowIfNull(request);

View File

@@ -0,0 +1,22 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Signals.Models;
namespace StellaOps.Signals.Services;
public interface ICallGraphSyncService
{
Task<CallGraphSyncResult> SyncAsync(CallGraphSyncRequest request, CancellationToken cancellationToken = default);
}
public sealed record CallGraphSyncRequest(
string ArtifactDigest,
string? SbomDigest,
string? RepoUri,
string? CommitSha,
string? PolicyDigest,
CallgraphDocument Document);
public sealed record CallGraphSyncResult(Guid ScanId, bool WasApplied);

View File

@@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Signals.Services;
internal sealed class NullCallGraphSyncService : ICallGraphSyncService
{
public Task<CallGraphSyncResult> SyncAsync(CallGraphSyncRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(new CallGraphSyncResult(ScanId: default, WasApplied: false));
}

View File

@@ -12,3 +12,4 @@ This file mirrors sprint work for the Signals module.
| `GATE-3405-011` | `docs/implplan/SPRINT_3405_0001_0001_gate_multipliers.md` | DONE (2025-12-18) | Applied gate multipliers in `ReachabilityScoringService` using path gate evidence from callgraph edges. |
| `GATE-3405-012` | `docs/implplan/SPRINT_3405_0001_0001_gate_multipliers.md` | DONE (2025-12-18) | Extended reachability fact evidence contract + digest to include `GateMultiplierBps` and `Gates`. |
| `GATE-3405-016` | `docs/implplan/SPRINT_3405_0001_0001_gate_multipliers.md` | DONE (2025-12-18) | Added deterministic parser/normalizer/scoring coverage for gate propagation + multiplier effect. |
| `SIG-CG-3104-003` | `docs/implplan/SPRINT_3104_0001_0001_signals_callgraph_projection_completion.md` | DONE (2025-12-18) | Added callgraph projection trigger via `ICallGraphSyncService` (default no-op implementation). |