up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-24 07:52:25 +02:00
parent 5970f0d9bd
commit 150b3730ef
215 changed files with 8119 additions and 740 deletions

View File

@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace StellaOps.Policy.Engine.TrustWeighting;
internal sealed record TrustWeightingEntry(
[property: JsonPropertyName("source")] string Source,
[property: JsonPropertyName("weight")] decimal Weight,
[property: JsonPropertyName("justification")] string? Justification,
[property: JsonPropertyName("updated_at")] string UpdatedAt);
internal sealed record TrustWeightingProfile(
[property: JsonPropertyName("weights")] IReadOnlyList<TrustWeightingEntry> Weights,
[property: JsonPropertyName("profile_hash")] string ProfileHash);

View File

@@ -0,0 +1,77 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace StellaOps.Policy.Engine.TrustWeighting;
/// <summary>
/// In-memory trust weighting profile store (stub for POLICY-ENGINE-30-101).
/// Deterministic ordering and hashing.
/// </summary>
internal sealed class TrustWeightingService
{
private readonly TimeProvider _timeProvider;
private readonly object _lock = new();
private TrustWeightingProfile _current;
public TrustWeightingService(TimeProvider timeProvider)
{
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
_current = BuildProfile(DefaultWeights());
}
public TrustWeightingProfile Get() => _current;
public TrustWeightingProfile Set(IReadOnlyList<TrustWeightingEntry> entries)
{
var normalized = Normalize(entries);
var profile = BuildProfile(normalized);
lock (_lock)
{
_current = profile;
}
return profile;
}
private TrustWeightingProfile BuildProfile(IReadOnlyList<TrustWeightingEntry> weights)
{
var json = JsonSerializer.Serialize(weights, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
});
var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(json)));
return new TrustWeightingProfile(weights, hash);
}
private IReadOnlyList<TrustWeightingEntry> Normalize(IReadOnlyList<TrustWeightingEntry> entries)
{
var now = _timeProvider.GetUtcNow().ToString("O");
var normalized = entries
.Where(e => !string.IsNullOrWhiteSpace(e.Source))
.Select(e => new TrustWeightingEntry(
Source: e.Source.Trim().ToLowerInvariant(),
Weight: Math.Round(e.Weight, 3, MidpointRounding.ToZero),
Justification: string.IsNullOrWhiteSpace(e.Justification) ? null : e.Justification.Trim(),
UpdatedAt: string.IsNullOrWhiteSpace(e.UpdatedAt) ? now : e.UpdatedAt))
.OrderBy(e => e.Source, StringComparer.Ordinal)
.ToList();
return normalized;
}
private static IReadOnlyList<TrustWeightingEntry> DefaultWeights()
{
var now = TimeProvider.System.GetUtcNow().ToString("O");
return new[]
{
new TrustWeightingEntry("cartographer", 1.000m, null, now),
new TrustWeightingEntry("concelier", 1.000m, null, now),
new TrustWeightingEntry("scanner", 1.000m, null, now),
new TrustWeightingEntry("advisory_ai", 1.000m, null, now)
};
}
}