up
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.Signals.Models.ReachabilityStore;
|
||||
|
||||
public sealed class CallEdgeDocument
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
public string GraphHash { get; set; } = string.Empty;
|
||||
|
||||
public string SourceId { get; set; } = string.Empty;
|
||||
|
||||
public string TargetId { get; set; } = string.Empty;
|
||||
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
public string? Purl { get; set; }
|
||||
|
||||
public string? SymbolDigest { get; set; }
|
||||
|
||||
public List<string>? Candidates { get; set; }
|
||||
|
||||
public double? Confidence { get; set; }
|
||||
|
||||
public List<string>? Evidence { get; set; }
|
||||
|
||||
public DateTimeOffset IngestedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.Signals.Models.ReachabilityStore;
|
||||
|
||||
public sealed class CveFuncHitDocument
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
public string SubjectKey { get; set; } = string.Empty;
|
||||
|
||||
public string CveId { get; set; } = string.Empty;
|
||||
|
||||
public string GraphHash { get; set; } = string.Empty;
|
||||
|
||||
public string? Purl { get; set; }
|
||||
|
||||
public string? SymbolDigest { get; set; }
|
||||
|
||||
public bool Reachable { get; set; }
|
||||
|
||||
public double? Confidence { get; set; }
|
||||
|
||||
public string? LatticeState { get; set; }
|
||||
|
||||
public List<string>? EvidenceUris { get; set; }
|
||||
|
||||
public DateTimeOffset ComputedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.Signals.Models.ReachabilityStore;
|
||||
|
||||
public sealed class FuncNodeDocument
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
public string GraphHash { get; set; } = string.Empty;
|
||||
|
||||
public string SymbolId { get; set; } = string.Empty;
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Kind { get; set; } = string.Empty;
|
||||
|
||||
public string? Namespace { get; set; }
|
||||
|
||||
public string? File { get; set; }
|
||||
|
||||
public int? Line { get; set; }
|
||||
|
||||
public string? Purl { get; set; }
|
||||
|
||||
public string? SymbolDigest { get; set; }
|
||||
|
||||
public string? BuildId { get; set; }
|
||||
|
||||
public string? CodeId { get; set; }
|
||||
|
||||
public string? Language { get; set; }
|
||||
|
||||
public List<string>? Evidence { get; set; }
|
||||
|
||||
public Dictionary<string, string?>? Analyzer { get; set; }
|
||||
|
||||
public DateTimeOffset IngestedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Models.ReachabilityStore;
|
||||
|
||||
namespace StellaOps.Signals.Persistence;
|
||||
|
||||
public interface IReachabilityStoreRepository
|
||||
{
|
||||
Task UpsertGraphAsync(
|
||||
string graphHash,
|
||||
IReadOnlyCollection<CallgraphNode> nodes,
|
||||
IReadOnlyCollection<CallgraphEdge> edges,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<IReadOnlyList<FuncNodeDocument>> GetFuncNodesByGraphAsync(string graphHash, CancellationToken cancellationToken);
|
||||
|
||||
Task<IReadOnlyList<CallEdgeDocument>> GetCallEdgesByGraphAsync(string graphHash, CancellationToken cancellationToken);
|
||||
|
||||
Task UpsertCveFuncHitsAsync(IReadOnlyCollection<CveFuncHitDocument> hits, CancellationToken cancellationToken);
|
||||
|
||||
Task<IReadOnlyList<CveFuncHitDocument>> GetCveFuncHitsBySubjectAsync(
|
||||
string subjectKey,
|
||||
string cveId,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Models.ReachabilityStore;
|
||||
|
||||
namespace StellaOps.Signals.Persistence;
|
||||
|
||||
internal sealed class InMemoryReachabilityStoreRepository : IReachabilityStoreRepository
|
||||
{
|
||||
private readonly TimeProvider timeProvider;
|
||||
private readonly ConcurrentDictionary<string, FuncNodeDocument> funcNodes = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, CallEdgeDocument> callEdges = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, CveFuncHitDocument> cveFuncHits = new(StringComparer.Ordinal);
|
||||
|
||||
public InMemoryReachabilityStoreRepository(TimeProvider timeProvider)
|
||||
{
|
||||
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
}
|
||||
|
||||
public Task UpsertGraphAsync(
|
||||
string graphHash,
|
||||
IReadOnlyCollection<CallgraphNode> nodes,
|
||||
IReadOnlyCollection<CallgraphEdge> edges,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(graphHash);
|
||||
ArgumentNullException.ThrowIfNull(nodes);
|
||||
ArgumentNullException.ThrowIfNull(edges);
|
||||
|
||||
var normalizedGraphHash = graphHash.Trim();
|
||||
var now = timeProvider.GetUtcNow();
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
var document = ToFuncNodeDocument(normalizedGraphHash, node, now);
|
||||
funcNodes[document.Id] = document;
|
||||
}
|
||||
|
||||
foreach (var edge in edges)
|
||||
{
|
||||
var document = ToCallEdgeDocument(normalizedGraphHash, edge, now);
|
||||
callEdges[document.Id] = document;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<FuncNodeDocument>> GetFuncNodesByGraphAsync(string graphHash, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(graphHash))
|
||||
{
|
||||
return Task.FromResult((IReadOnlyList<FuncNodeDocument>)Array.Empty<FuncNodeDocument>());
|
||||
}
|
||||
|
||||
var normalizedGraphHash = graphHash.Trim();
|
||||
|
||||
var results = funcNodes.Values
|
||||
.Where(doc => string.Equals(doc.GraphHash, normalizedGraphHash, StringComparison.Ordinal))
|
||||
.Select(Clone)
|
||||
.OrderBy(doc => doc.SymbolId, StringComparer.Ordinal)
|
||||
.ThenBy(doc => doc.Purl, StringComparer.Ordinal)
|
||||
.ThenBy(doc => doc.SymbolDigest, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult((IReadOnlyList<FuncNodeDocument>)results);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<CallEdgeDocument>> GetCallEdgesByGraphAsync(string graphHash, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(graphHash))
|
||||
{
|
||||
return Task.FromResult((IReadOnlyList<CallEdgeDocument>)Array.Empty<CallEdgeDocument>());
|
||||
}
|
||||
|
||||
var normalizedGraphHash = graphHash.Trim();
|
||||
|
||||
var results = callEdges.Values
|
||||
.Where(doc => string.Equals(doc.GraphHash, normalizedGraphHash, StringComparison.Ordinal))
|
||||
.Select(Clone)
|
||||
.OrderBy(doc => doc.SourceId, StringComparer.Ordinal)
|
||||
.ThenBy(doc => doc.TargetId, StringComparer.Ordinal)
|
||||
.ThenBy(doc => doc.Type, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult((IReadOnlyList<CallEdgeDocument>)results);
|
||||
}
|
||||
|
||||
public Task UpsertCveFuncHitsAsync(IReadOnlyCollection<CveFuncHitDocument> hits, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(hits);
|
||||
|
||||
foreach (var hit in hits.Where(h => h is not null))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(hit.SubjectKey) || string.IsNullOrWhiteSpace(hit.CveId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var id = BuildCveFuncHitId(hit.SubjectKey, hit.CveId, hit.Purl, hit.SymbolDigest);
|
||||
var clone = Clone(hit);
|
||||
clone.Id = id;
|
||||
cveFuncHits[id] = clone;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<CveFuncHitDocument>> GetCveFuncHitsBySubjectAsync(
|
||||
string subjectKey,
|
||||
string cveId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(subjectKey) || string.IsNullOrWhiteSpace(cveId))
|
||||
{
|
||||
return Task.FromResult((IReadOnlyList<CveFuncHitDocument>)Array.Empty<CveFuncHitDocument>());
|
||||
}
|
||||
|
||||
var normalizedSubjectKey = subjectKey.Trim();
|
||||
var normalizedCve = cveId.Trim();
|
||||
|
||||
var results = cveFuncHits.Values
|
||||
.Where(doc =>
|
||||
string.Equals(doc.SubjectKey, normalizedSubjectKey, StringComparison.Ordinal) &&
|
||||
string.Equals(doc.CveId, normalizedCve, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(Clone)
|
||||
.OrderBy(doc => doc.CveId, StringComparer.OrdinalIgnoreCase)
|
||||
.ThenBy(doc => doc.Purl, StringComparer.Ordinal)
|
||||
.ThenBy(doc => doc.SymbolDigest, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult((IReadOnlyList<CveFuncHitDocument>)results);
|
||||
}
|
||||
|
||||
private static FuncNodeDocument ToFuncNodeDocument(string graphHash, CallgraphNode node, DateTimeOffset ingestedAt)
|
||||
{
|
||||
var symbolId = node.Id?.Trim() ?? string.Empty;
|
||||
var id = BuildFuncNodeId(graphHash, symbolId);
|
||||
|
||||
return new FuncNodeDocument
|
||||
{
|
||||
Id = id,
|
||||
GraphHash = graphHash,
|
||||
SymbolId = symbolId,
|
||||
Name = node.Name?.Trim() ?? string.Empty,
|
||||
Kind = node.Kind?.Trim() ?? string.Empty,
|
||||
Namespace = node.Namespace?.Trim(),
|
||||
File = node.File?.Trim(),
|
||||
Line = node.Line,
|
||||
Purl = node.Purl?.Trim(),
|
||||
SymbolDigest = node.SymbolDigest?.Trim()?.ToLowerInvariant(),
|
||||
BuildId = node.BuildId?.Trim(),
|
||||
CodeId = node.CodeId?.Trim(),
|
||||
Language = node.Language?.Trim(),
|
||||
Evidence = node.Evidence?.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).OrderBy(v => v, StringComparer.Ordinal).ToList(),
|
||||
Analyzer = node.Analyzer is null
|
||||
? null
|
||||
: node.Analyzer
|
||||
.Where(kv => !string.IsNullOrWhiteSpace(kv.Key))
|
||||
.OrderBy(kv => kv.Key, StringComparer.Ordinal)
|
||||
.ToDictionary(kv => kv.Key.Trim(), kv => kv.Value?.Trim(), StringComparer.Ordinal),
|
||||
IngestedAt = ingestedAt
|
||||
};
|
||||
}
|
||||
|
||||
private static CallEdgeDocument ToCallEdgeDocument(string graphHash, CallgraphEdge edge, DateTimeOffset ingestedAt)
|
||||
{
|
||||
var sourceId = edge.SourceId?.Trim() ?? string.Empty;
|
||||
var targetId = edge.TargetId?.Trim() ?? string.Empty;
|
||||
var type = edge.Type?.Trim() ?? string.Empty;
|
||||
var id = BuildCallEdgeId(graphHash, sourceId, targetId, type);
|
||||
|
||||
return new CallEdgeDocument
|
||||
{
|
||||
Id = id,
|
||||
GraphHash = graphHash,
|
||||
SourceId = sourceId,
|
||||
TargetId = targetId,
|
||||
Type = type,
|
||||
Purl = edge.Purl?.Trim(),
|
||||
SymbolDigest = edge.SymbolDigest?.Trim()?.ToLowerInvariant(),
|
||||
Candidates = edge.Candidates?.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).OrderBy(v => v, StringComparer.Ordinal).ToList(),
|
||||
Confidence = edge.Confidence,
|
||||
Evidence = edge.Evidence?.Where(v => !string.IsNullOrWhiteSpace(v)).Select(v => v.Trim()).OrderBy(v => v, StringComparer.Ordinal).ToList(),
|
||||
IngestedAt = ingestedAt
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildFuncNodeId(string graphHash, string symbolId)
|
||||
=> $"{graphHash}|{symbolId}";
|
||||
|
||||
private static string BuildCallEdgeId(string graphHash, string sourceId, string targetId, string type)
|
||||
=> $"{graphHash}|{sourceId}->{targetId}|{type}";
|
||||
|
||||
private static string BuildCveFuncHitId(string subjectKey, string cveId, string? purl, string? symbolDigest)
|
||||
=> $"{subjectKey.Trim()}|{cveId.Trim().ToUpperInvariant()}|{purl?.Trim() ?? string.Empty}|{symbolDigest?.Trim()?.ToLowerInvariant() ?? string.Empty}";
|
||||
|
||||
private static FuncNodeDocument Clone(FuncNodeDocument source) => new()
|
||||
{
|
||||
Id = source.Id,
|
||||
GraphHash = source.GraphHash,
|
||||
SymbolId = source.SymbolId,
|
||||
Name = source.Name,
|
||||
Kind = source.Kind,
|
||||
Namespace = source.Namespace,
|
||||
File = source.File,
|
||||
Line = source.Line,
|
||||
Purl = source.Purl,
|
||||
SymbolDigest = source.SymbolDigest,
|
||||
BuildId = source.BuildId,
|
||||
CodeId = source.CodeId,
|
||||
Language = source.Language,
|
||||
Evidence = source.Evidence?.ToList(),
|
||||
Analyzer = source.Analyzer is null ? null : new Dictionary<string, string?>(source.Analyzer, StringComparer.Ordinal),
|
||||
IngestedAt = source.IngestedAt
|
||||
};
|
||||
|
||||
private static CallEdgeDocument Clone(CallEdgeDocument source) => new()
|
||||
{
|
||||
Id = source.Id,
|
||||
GraphHash = source.GraphHash,
|
||||
SourceId = source.SourceId,
|
||||
TargetId = source.TargetId,
|
||||
Type = source.Type,
|
||||
Purl = source.Purl,
|
||||
SymbolDigest = source.SymbolDigest,
|
||||
Candidates = source.Candidates?.ToList(),
|
||||
Confidence = source.Confidence,
|
||||
Evidence = source.Evidence?.ToList(),
|
||||
IngestedAt = source.IngestedAt
|
||||
};
|
||||
|
||||
private static CveFuncHitDocument Clone(CveFuncHitDocument source) => new()
|
||||
{
|
||||
Id = source.Id,
|
||||
SubjectKey = source.SubjectKey,
|
||||
CveId = source.CveId,
|
||||
GraphHash = source.GraphHash,
|
||||
Purl = source.Purl,
|
||||
SymbolDigest = source.SymbolDigest,
|
||||
Reachable = source.Reachable,
|
||||
Confidence = source.Confidence,
|
||||
LatticeState = source.LatticeState,
|
||||
EvidenceUris = source.EvidenceUris?.ToList(),
|
||||
ComputedAt = source.ComputedAt
|
||||
};
|
||||
}
|
||||
@@ -132,6 +132,7 @@ builder.Services.AddSingleton<IReachabilityFactRepository>(sp =>
|
||||
return new ReachabilityFactCacheDecorator(inner, cache);
|
||||
});
|
||||
builder.Services.AddSingleton<IUnknownsRepository, InMemoryUnknownsRepository>();
|
||||
builder.Services.AddSingleton<IReachabilityStoreRepository, InMemoryReachabilityStoreRepository>();
|
||||
builder.Services.AddHttpClient<RouterEventsPublisher>((sp, client) =>
|
||||
{
|
||||
var opts = sp.GetRequiredService<SignalsOptions>().Events.Router;
|
||||
|
||||
@@ -29,6 +29,7 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
|
||||
private readonly ICallgraphParserResolver parserResolver;
|
||||
private readonly ICallgraphArtifactStore artifactStore;
|
||||
private readonly ICallgraphRepository repository;
|
||||
private readonly IReachabilityStoreRepository reachabilityStore;
|
||||
private readonly ICallgraphNormalizationService normalizer;
|
||||
private readonly ILogger<CallgraphIngestionService> logger;
|
||||
private readonly SignalsOptions options;
|
||||
@@ -39,6 +40,7 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
|
||||
ICallgraphParserResolver parserResolver,
|
||||
ICallgraphArtifactStore artifactStore,
|
||||
ICallgraphRepository repository,
|
||||
IReachabilityStoreRepository reachabilityStore,
|
||||
ICallgraphNormalizationService normalizer,
|
||||
IOptions<SignalsOptions> options,
|
||||
TimeProvider timeProvider,
|
||||
@@ -47,6 +49,7 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
|
||||
this.parserResolver = parserResolver ?? throw new ArgumentNullException(nameof(parserResolver));
|
||||
this.artifactStore = artifactStore ?? throw new ArgumentNullException(nameof(artifactStore));
|
||||
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.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
@@ -143,6 +146,12 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
|
||||
|
||||
document = await repository.UpsertAsync(document, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await reachabilityStore.UpsertGraphAsync(
|
||||
document.GraphHash,
|
||||
document.Nodes,
|
||||
document.Edges,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
logger.LogInformation(
|
||||
"Ingested callgraph {Language}:{Component}:{Version} (id={Id}) with {NodeCount} nodes and {EdgeCount} edges.",
|
||||
document.Language,
|
||||
|
||||
@@ -157,8 +157,12 @@ public sealed class ReachabilityScoringService : IReachabilityScoringService
|
||||
var finalScore = baseScore * (1 - pressurePenalty);
|
||||
|
||||
var uncertaintyStates = MergeUncertaintyStates(existingFact?.Uncertainty?.States, unknownsCount, pressure, states.Count, computedAt);
|
||||
var (uncertainty, aggregateTier) = BuildUncertaintyDocument(uncertaintyStates, baseScore, computedAt);
|
||||
var riskScore = ComputeRiskScoreWithTiers(baseScore, uncertaintyStates, aggregateTier);
|
||||
var (uncertainty, _, riskScore) = BuildUncertaintyDocument(
|
||||
uncertaintyStates,
|
||||
baseScore,
|
||||
computedAt,
|
||||
scoringOptions.UncertaintyEntropyMultiplier,
|
||||
scoringOptions.UncertaintyBoostCeiling);
|
||||
|
||||
var document = new ReachabilityFactDocument
|
||||
{
|
||||
@@ -252,14 +256,16 @@ public sealed class ReachabilityScoringService : IReachabilityScoringService
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static (UncertaintyDocument? Document, UncertaintyTier AggregateTier) BuildUncertaintyDocument(
|
||||
private static (UncertaintyDocument? Document, UncertaintyTier AggregateTier, double RiskScore) BuildUncertaintyDocument(
|
||||
List<UncertaintyStateDocument> states,
|
||||
double baseScore,
|
||||
DateTimeOffset computedAt)
|
||||
DateTimeOffset computedAt,
|
||||
double entropyMultiplier,
|
||||
double boostCeiling)
|
||||
{
|
||||
if (states.Count == 0)
|
||||
{
|
||||
return (null, UncertaintyTier.T4);
|
||||
return (null, UncertaintyTier.T4, baseScore);
|
||||
}
|
||||
|
||||
// Calculate aggregate tier
|
||||
@@ -270,7 +276,12 @@ public sealed class ReachabilityScoringService : IReachabilityScoringService
|
||||
var meanEntropy = states.Average(s => s.Entropy);
|
||||
|
||||
// Calculate risk score with tier modifiers
|
||||
var riskScore = UncertaintyTierCalculator.CalculateRiskScore(baseScore, aggregateTier, meanEntropy);
|
||||
var riskScore = UncertaintyTierCalculator.CalculateRiskScore(
|
||||
baseScore,
|
||||
aggregateTier,
|
||||
meanEntropy,
|
||||
entropyMultiplier,
|
||||
boostCeiling);
|
||||
|
||||
var document = new UncertaintyDocument
|
||||
{
|
||||
@@ -280,7 +291,7 @@ public sealed class ReachabilityScoringService : IReachabilityScoringService
|
||||
ComputedAt = computedAt
|
||||
};
|
||||
|
||||
return (document, aggregateTier);
|
||||
return (document, aggregateTier, riskScore);
|
||||
}
|
||||
|
||||
private static UncertaintyStateDocument NormalizeState(UncertaintyStateDocument state)
|
||||
@@ -315,23 +326,6 @@ public sealed class ReachabilityScoringService : IReachabilityScoringService
|
||||
};
|
||||
}
|
||||
|
||||
private double ComputeRiskScoreWithTiers(
|
||||
double baseScore,
|
||||
IReadOnlyList<UncertaintyStateDocument> uncertaintyStates,
|
||||
UncertaintyTier aggregateTier)
|
||||
{
|
||||
var meanEntropy = uncertaintyStates.Count > 0
|
||||
? uncertaintyStates.Average(s => s.Entropy)
|
||||
: 0.0;
|
||||
|
||||
return UncertaintyTierCalculator.CalculateRiskScore(
|
||||
baseScore,
|
||||
aggregateTier,
|
||||
meanEntropy,
|
||||
scoringOptions.UncertaintyEntropyMultiplier,
|
||||
scoringOptions.UncertaintyBoostCeiling);
|
||||
}
|
||||
|
||||
private static void ValidateRequest(ReachabilityRecomputeRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.CallgraphId))
|
||||
|
||||
@@ -4,4 +4,6 @@ This file mirrors sprint work for the Signals module.
|
||||
|
||||
| Task ID | Sprint | Status | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `UNCERTAINTY-SCHEMA-401-024` | `docs/implplan/SPRINT_0401_0001_0001_reachability_evidence_chain.md` | DOING | Add uncertainty states + entropy-derived `riskScore` to reachability facts and update events/tests. |
|
||||
| `SIG-STORE-401-016` | `docs/implplan/SPRINT_0401_0001_0001_reachability_evidence_chain.md` | DONE (2025-12-13) | Added reachability store repository APIs and models; callgraph ingestion now populates the store; Mongo index script at `ops/mongo/indices/reachability_store_indices.js`. |
|
||||
| `UNCERTAINTY-SCHEMA-401-024` | `docs/implplan/SPRINT_0401_0001_0001_reachability_evidence_chain.md` | DONE (2025-12-13) | Implemented uncertainty tiers and scoring integration; see `src/Signals/StellaOps.Signals/Lattice/UncertaintyTier.cs` and `src/Signals/StellaOps.Signals/Lattice/ReachabilityLattice.cs`. |
|
||||
| `UNCERTAINTY-SCORER-401-025` | `docs/implplan/SPRINT_0401_0001_0001_reachability_evidence_chain.md` | DONE (2025-12-13) | Reachability risk score now uses configurable entropy weights and is aligned with `UncertaintyDocument.RiskScore`; tests cover tier/entropy scoring. |
|
||||
|
||||
Reference in New Issue
Block a user