consolidation of some of the modules, localization fixes, product advisories work, qa work

This commit is contained in:
master
2026-03-05 03:54:22 +02:00
parent 7bafcc3eef
commit 8e1cb9448d
3878 changed files with 72600 additions and 46861 deletions

View File

@@ -0,0 +1,19 @@
namespace StellaOps.Symbols.Marketplace.Models;
/// <summary>
/// A catalog entry representing an installable symbol/debug pack.
/// </summary>
public sealed record SymbolPackCatalogEntry
{
public Guid Id { get; init; }
public Guid SourceId { get; init; }
public string PackId { get; init; } = string.Empty;
public string Platform { get; init; } = string.Empty;
public string[] Components { get; init; } = [];
public string DsseDigest { get; init; } = string.Empty;
public string Version { get; init; } = string.Empty;
public long SizeBytes { get; init; }
public bool Installed { get; init; }
public DateTimeOffset PublishedAt { get; init; }
public DateTimeOffset? InstalledAt { get; init; }
}

View File

@@ -0,0 +1,19 @@
namespace StellaOps.Symbols.Marketplace.Models;
/// <summary>
/// Registry of symbol providers (vendor/distro/community/partner).
/// </summary>
public sealed record SymbolPackSource
{
public Guid Id { get; init; }
public string Key { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string SourceType { get; init; } = string.Empty;
public string? Url { get; init; }
public int Priority { get; init; }
public bool Enabled { get; init; } = true;
public int FreshnessSlaSeconds { get; init; } = 21600;
public decimal WarningRatio { get; init; } = 0.80m;
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? UpdatedAt { get; init; }
}

View File

@@ -0,0 +1,27 @@
namespace StellaOps.Symbols.Marketplace.Models;
/// <summary>
/// Freshness projection for a symbol source, mirroring the AdvisorySourceFreshnessRecord pattern.
/// </summary>
public sealed record SymbolSourceFreshnessRecord(
Guid SourceId,
string SourceKey,
string SourceName,
string SourceType,
string? SourceUrl,
int Priority,
bool Enabled,
DateTimeOffset? LastSyncAt,
DateTimeOffset? LastSuccessAt,
string? LastError,
long SyncCount,
int ErrorCount,
int FreshnessSlaSeconds,
decimal WarningRatio,
long FreshnessAgeSeconds,
string FreshnessStatus,
string SignatureStatus,
long TotalPacks,
long SignedPacks,
long UnsignedPacks,
long SignatureFailureCount);

View File

@@ -0,0 +1,12 @@
namespace StellaOps.Symbols.Marketplace.Models;
/// <summary>
/// Four-dimension trust score for a symbol source.
/// Each dimension is 0.0 to 1.0; Overall is a weighted average.
/// </summary>
public sealed record SymbolSourceTrustScore(
double Freshness,
double Signature,
double Coverage,
double SlCompliance,
double Overall);

View File

@@ -0,0 +1,34 @@
using StellaOps.Symbols.Marketplace.Models;
namespace StellaOps.Symbols.Marketplace.Repositories;
/// <summary>
/// Repository for marketplace catalog operations (browse, install, uninstall).
/// </summary>
public interface IMarketplaceCatalogRepository
{
Task<IReadOnlyList<SymbolPackCatalogEntry>> ListCatalogAsync(
Guid? sourceId,
string? search,
int limit,
int offset,
CancellationToken cancellationToken = default);
Task<SymbolPackCatalogEntry?> GetCatalogEntryAsync(
Guid entryId,
CancellationToken cancellationToken = default);
Task InstallPackAsync(
Guid entryId,
string tenantId,
CancellationToken cancellationToken = default);
Task UninstallPackAsync(
Guid entryId,
string tenantId,
CancellationToken cancellationToken = default);
Task<IReadOnlyList<SymbolPackCatalogEntry>> ListInstalledAsync(
string tenantId,
CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,17 @@
using StellaOps.Symbols.Marketplace.Models;
namespace StellaOps.Symbols.Marketplace.Repositories;
/// <summary>
/// Read repository for symbol sources and their freshness projections.
/// </summary>
public interface ISymbolSourceReadRepository
{
Task<IReadOnlyList<SymbolSourceFreshnessRecord>> ListSourcesAsync(
bool includeDisabled,
CancellationToken cancellationToken = default);
Task<SymbolSourceFreshnessRecord?> GetSourceByIdAsync(
Guid sourceId,
CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,105 @@
using StellaOps.Symbols.Marketplace.Models;
namespace StellaOps.Symbols.Marketplace.Scoring;
/// <summary>
/// Default trust scorer using weighted dimensions:
/// Freshness=0.3, Signature=0.3, Coverage=0.2, SLA=0.2.
/// </summary>
public sealed class DefaultSymbolSourceTrustScorer : ISymbolSourceTrustScorer
{
private const double WeightFreshness = 0.3;
private const double WeightSignature = 0.3;
private const double WeightCoverage = 0.2;
private const double WeightSla = 0.2;
public SymbolSourceTrustScore CalculateTrust(SymbolSourceFreshnessRecord source)
{
ArgumentNullException.ThrowIfNull(source);
var freshness = ComputeFreshnessDimension(source);
var signature = ComputeSignatureDimension(source);
var coverage = ComputeCoverageDimension(source);
var slCompliance = ComputeSlaDimension(source);
var overall =
(freshness * WeightFreshness) +
(signature * WeightSignature) +
(coverage * WeightCoverage) +
(slCompliance * WeightSla);
return new SymbolSourceTrustScore(
Freshness: Clamp(freshness),
Signature: Clamp(signature),
Coverage: Clamp(coverage),
SlCompliance: Clamp(slCompliance),
Overall: Clamp(overall));
}
private static double ComputeFreshnessDimension(SymbolSourceFreshnessRecord source)
{
if (source.FreshnessSlaSeconds <= 0)
{
return 0.0;
}
return source.FreshnessStatus switch
{
"healthy" => 1.0,
"warning" => 1.0 - ((double)source.FreshnessAgeSeconds / source.FreshnessSlaSeconds),
"stale" => Math.Max(0.0, 0.3 - (0.1 * ((double)source.FreshnessAgeSeconds / source.FreshnessSlaSeconds - 1.0))),
_ => 0.0 // unavailable
};
}
private static double ComputeSignatureDimension(SymbolSourceFreshnessRecord source)
{
if (source.TotalPacks <= 0)
{
return 0.0;
}
return (double)source.SignedPacks / source.TotalPacks;
}
private static double ComputeCoverageDimension(SymbolSourceFreshnessRecord source)
{
// Coverage is derived from the presence of packs relative to sync activity.
// A source with packs and no errors has full coverage potential.
if (source.SyncCount <= 0)
{
return 0.0;
}
if (source.TotalPacks <= 0)
{
return 0.0;
}
var errorRate = source.SyncCount > 0
? (double)source.ErrorCount / source.SyncCount
: 0.0;
return Math.Max(0.0, 1.0 - errorRate);
}
private static double ComputeSlaDimension(SymbolSourceFreshnessRecord source)
{
if (source.FreshnessSlaSeconds <= 0)
{
return 0.0;
}
// SLA compliance is based on whether the source stays within its freshness window.
if (source.FreshnessAgeSeconds <= source.FreshnessSlaSeconds)
{
return 1.0;
}
// Degrade linearly up to 2x the SLA window, then zero.
var overage = (double)(source.FreshnessAgeSeconds - source.FreshnessSlaSeconds) / source.FreshnessSlaSeconds;
return Math.Max(0.0, 1.0 - overage);
}
private static double Clamp(double value) => Math.Clamp(value, 0.0, 1.0);
}

View File

@@ -0,0 +1,12 @@
using StellaOps.Symbols.Marketplace.Models;
namespace StellaOps.Symbols.Marketplace.Scoring;
/// <summary>
/// Computes a trust score for a symbol source based on freshness, signature coverage,
/// artifact coverage, and SLA compliance.
/// </summary>
public interface ISymbolSourceTrustScorer
{
SymbolSourceTrustScore CalculateTrust(SymbolSourceFreshnessRecord source);
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>