feat: add PolicyPackSelectorComponent with tests and integration

- Implemented PolicyPackSelectorComponent for selecting policy packs.
- Added unit tests for component behavior, including API success and error handling.
- Introduced monaco-workers type declarations for editor workers.
- Created acceptance tests for guardrails with stubs for AT1–AT10.
- Established SCA Failure Catalogue Fixtures for regression testing.
- Developed plugin determinism harness with stubs for PL1–PL10.
- Added scripts for evidence upload and verification processes.
This commit is contained in:
StellaOps Bot
2025-12-05 21:24:34 +02:00
parent 347c88342c
commit 18d87c64c5
220 changed files with 7700 additions and 518 deletions

View File

@@ -40,7 +40,7 @@ internal static class TaskPackPlanHasher
var json = CanonicalJson.Serialize(canonical);
using var sha256 = SHA256.Create();
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(json));
return ConvertToHex(hashBytes);
return $"sha256:{ConvertToHex(hashBytes)}";
}
private static string ConvertToHex(byte[] hashBytes)

View File

@@ -22,16 +22,17 @@ public sealed class TaskPackPlanner
this.egressPolicy = egressPolicy;
}
public TaskPackPlanResult Plan(TaskPackManifest manifest, IDictionary<string, JsonNode?>? providedInputs = null)
{
ArgumentNullException.ThrowIfNull(manifest);
var errors = ImmutableArray.CreateBuilder<TaskPackPlanError>();
var validation = validator.Validate(manifest);
if (!validation.IsValid)
{
foreach (var error in validation.Errors)
public TaskPackPlanResult Plan(TaskPackManifest manifest, IDictionary<string, JsonNode?>? providedInputs = null)
{
ArgumentNullException.ThrowIfNull(manifest);
var errors = ImmutableArray.CreateBuilder<TaskPackPlanError>();
ValidateSandboxAndSlo(manifest, errors);
var validation = validator.Validate(manifest);
if (!validation.IsValid)
{
foreach (var error in validation.Errors)
{
errors.Add(new TaskPackPlanError(error.Path, error.Message));
}
@@ -106,10 +107,70 @@ public sealed class TaskPackPlanner
return new TaskPackPlanResult(plan, ImmutableArray<TaskPackPlanError>.Empty);
}
private static void ValidateSandboxAndSlo(TaskPackManifest manifest, ImmutableArray<TaskPackPlanError>.Builder errors)
{
// TP6: sandbox quotas must be present.
var sandbox = manifest.Spec.Sandbox;
if (sandbox is null)
{
errors.Add(new TaskPackPlanError("spec.sandbox", "Sandbox settings are required (mode, egressAllowlist, CPU/memory, quotaSeconds)."));
}
else
{
if (string.IsNullOrWhiteSpace(sandbox.Mode))
{
errors.Add(new TaskPackPlanError("spec.sandbox.mode", "Sandbox mode is required (sealed or restricted)."));
}
if (sandbox.EgressAllowlist is null)
{
errors.Add(new TaskPackPlanError("spec.sandbox.egressAllowlist", "Egress allowlist must be declared (empty list allowed)."));
}
if (sandbox.CpuLimitMillicores <= 0)
{
errors.Add(new TaskPackPlanError("spec.sandbox.cpuLimitMillicores", "CPU limit must be > 0."));
}
if (sandbox.MemoryLimitMiB <= 0)
{
errors.Add(new TaskPackPlanError("spec.sandbox.memoryLimitMiB", "Memory limit must be > 0."));
}
if (sandbox.QuotaSeconds <= 0)
{
errors.Add(new TaskPackPlanError("spec.sandbox.quotaSeconds", "quotaSeconds must be > 0."));
}
}
// TP9: SLOs must be declared and positive.
var slo = manifest.Spec.Slo;
if (slo is null)
{
errors.Add(new TaskPackPlanError("spec.slo", "SLO section is required (runP95Seconds, approvalP95Seconds, maxQueueDepth)."));
return;
}
if (slo.RunP95Seconds <= 0)
{
errors.Add(new TaskPackPlanError("spec.slo.runP95Seconds", "runP95Seconds must be > 0."));
}
if (slo.ApprovalP95Seconds <= 0)
{
errors.Add(new TaskPackPlanError("spec.slo.approvalP95Seconds", "approvalP95Seconds must be > 0."));
}
if (slo.MaxQueueDepth <= 0)
{
errors.Add(new TaskPackPlanError("spec.slo.maxQueueDepth", "maxQueueDepth must be > 0."));
}
}
private Dictionary<string, JsonNode?> MaterializeInputs(
IReadOnlyList<TaskPackInput>? definitions,
IDictionary<string, JsonNode?>? providedInputs,
ImmutableArray<TaskPackPlanError>.Builder errors)
IDictionary<string, JsonNode?>? providedInputs,
ImmutableArray<TaskPackPlanError>.Builder errors)
{
var effective = new Dictionary<string, JsonNode?>(StringComparer.Ordinal);

View File

@@ -54,11 +54,11 @@ public sealed class TaskPackMaintainer
public string? Email { get; init; }
}
public sealed class TaskPackSpec
{
[JsonPropertyName("inputs")]
public IReadOnlyList<TaskPackInput>? Inputs { get; init; }
public sealed class TaskPackSpec
{
[JsonPropertyName("inputs")]
public IReadOnlyList<TaskPackInput>? Inputs { get; init; }
[JsonPropertyName("secrets")]
public IReadOnlyList<TaskPackSecret>? Secrets { get; init; }
@@ -72,11 +72,17 @@ public sealed class TaskPackSpec
public IReadOnlyList<TaskPackOutput>? Outputs { get; init; }
[JsonPropertyName("success")]
public TaskPackSuccess? Success { get; init; }
[JsonPropertyName("failure")]
public TaskPackFailure? Failure { get; init; }
}
public TaskPackSuccess? Success { get; init; }
[JsonPropertyName("failure")]
public TaskPackFailure? Failure { get; init; }
[JsonPropertyName("sandbox")]
public TaskPackSandbox? Sandbox { get; init; }
[JsonPropertyName("slo")]
public TaskPackSlo? Slo { get; init; }
}
public sealed class TaskPackInput
{
@@ -255,11 +261,41 @@ public sealed class TaskPackFailure
public TaskPackRetryPolicy? Retries { get; init; }
}
public sealed class TaskPackRetryPolicy
{
[JsonPropertyName("maxAttempts")]
public int MaxAttempts { get; init; }
[JsonPropertyName("backoffSeconds")]
public int BackoffSeconds { get; init; }
}
public sealed class TaskPackRetryPolicy
{
[JsonPropertyName("maxAttempts")]
public int MaxAttempts { get; init; }
[JsonPropertyName("backoffSeconds")]
public int BackoffSeconds { get; init; }
}
public sealed class TaskPackSandbox
{
[JsonPropertyName("mode")]
public string? Mode { get; init; }
[JsonPropertyName("egressAllowlist")]
public IReadOnlyList<string>? EgressAllowlist { get; init; }
[JsonPropertyName("cpuLimitMillicores")]
public int CpuLimitMillicores { get; init; }
[JsonPropertyName("memoryLimitMiB")]
public int MemoryLimitMiB { get; init; }
[JsonPropertyName("quotaSeconds")]
public int QuotaSeconds { get; init; }
}
public sealed class TaskPackSlo
{
[JsonPropertyName("runP95Seconds")]
public int RunP95Seconds { get; init; }
[JsonPropertyName("approvalP95Seconds")]
public int ApprovalP95Seconds { get; init; }
[JsonPropertyName("maxQueueDepth")]
public int MaxQueueDepth { get; init; }
}