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
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (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
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
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
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
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (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
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
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using StellaOps.Scanner.Reachability;
|
||||
|
||||
namespace StellaOps.Reachability.FixtureTests.PatchOracle;
|
||||
|
||||
/// <summary>
|
||||
/// Compares a RichGraph against a patch-oracle definition.
|
||||
/// Reports missing expected elements and present forbidden elements.
|
||||
/// </summary>
|
||||
public sealed class PatchOracleComparer
|
||||
{
|
||||
private readonly PatchOracleDefinition _oracle;
|
||||
private readonly double _defaultMinConfidence;
|
||||
|
||||
public PatchOracleComparer(PatchOracleDefinition oracle)
|
||||
{
|
||||
_oracle = oracle ?? throw new ArgumentNullException(nameof(oracle));
|
||||
_defaultMinConfidence = oracle.MinConfidence;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the graph against the oracle and returns a result.
|
||||
/// </summary>
|
||||
public PatchOracleResult Compare(RichGraph graph)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(graph);
|
||||
|
||||
var violations = new List<PatchOracleViolation>();
|
||||
|
||||
// Check expected functions
|
||||
foreach (var expected in _oracle.ExpectedFunctions.Where(f => f.Required))
|
||||
{
|
||||
if (!HasMatchingNode(graph, expected))
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.MissingFunction,
|
||||
expected.SymbolId,
|
||||
null,
|
||||
expected.Reason ?? $"Expected function '{expected.SymbolId}' not found in graph"));
|
||||
}
|
||||
}
|
||||
|
||||
// Check expected edges
|
||||
foreach (var expected in _oracle.ExpectedEdges.Where(e => e.Required))
|
||||
{
|
||||
var minConf = expected.MinConfidence ?? _defaultMinConfidence;
|
||||
if (!HasMatchingEdge(graph, expected, minConf))
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.MissingEdge,
|
||||
expected.From,
|
||||
expected.To,
|
||||
expected.Reason ?? $"Expected edge '{expected.From}' -> '{expected.To}' not found in graph"));
|
||||
}
|
||||
}
|
||||
|
||||
// Check expected roots
|
||||
foreach (var expected in _oracle.ExpectedRoots.Where(r => r.Required))
|
||||
{
|
||||
if (!HasMatchingRoot(graph, expected))
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.MissingRoot,
|
||||
expected.Id,
|
||||
null,
|
||||
expected.Reason ?? $"Expected root '{expected.Id}' not found in graph"));
|
||||
}
|
||||
}
|
||||
|
||||
// Check forbidden functions
|
||||
foreach (var forbidden in _oracle.ForbiddenFunctions)
|
||||
{
|
||||
if (HasMatchingNode(graph, forbidden))
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.ForbiddenFunctionPresent,
|
||||
forbidden.SymbolId,
|
||||
null,
|
||||
forbidden.Reason ?? $"Forbidden function '{forbidden.SymbolId}' is present in graph"));
|
||||
}
|
||||
}
|
||||
|
||||
// Check forbidden edges
|
||||
foreach (var forbidden in _oracle.ForbiddenEdges)
|
||||
{
|
||||
if (HasMatchingEdge(graph, forbidden, 0.0))
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.ForbiddenEdgePresent,
|
||||
forbidden.From,
|
||||
forbidden.To,
|
||||
forbidden.Reason ?? $"Forbidden edge '{forbidden.From}' -> '{forbidden.To}' is present in graph"));
|
||||
}
|
||||
}
|
||||
|
||||
// Strict mode: check for unexpected elements
|
||||
if (_oracle.StrictMode)
|
||||
{
|
||||
var unexpectedNodes = FindUnexpectedNodes(graph);
|
||||
foreach (var node in unexpectedNodes)
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.UnexpectedFunction,
|
||||
node.Id,
|
||||
null,
|
||||
$"Strict mode: unexpected function '{node.Id}' found in graph"));
|
||||
}
|
||||
|
||||
var unexpectedEdges = FindUnexpectedEdges(graph);
|
||||
foreach (var edge in unexpectedEdges)
|
||||
{
|
||||
violations.Add(new PatchOracleViolation(
|
||||
ViolationType.UnexpectedEdge,
|
||||
edge.From,
|
||||
edge.To,
|
||||
$"Strict mode: unexpected edge '{edge.From}' -> '{edge.To}' found in graph"));
|
||||
}
|
||||
}
|
||||
|
||||
return new PatchOracleResult(
|
||||
OracleId: _oracle.Id,
|
||||
CaseRef: _oracle.CaseRef,
|
||||
Variant: _oracle.Variant,
|
||||
Success: violations.Count == 0,
|
||||
Violations: violations,
|
||||
Summary: GenerateSummary(graph, violations));
|
||||
}
|
||||
|
||||
private bool HasMatchingNode(RichGraph graph, ExpectedFunction expected)
|
||||
{
|
||||
foreach (var node in graph.Nodes)
|
||||
{
|
||||
if (!MatchesPattern(node.Id, expected.SymbolId) &&
|
||||
!MatchesPattern(node.SymbolId, expected.SymbolId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expected.Lang) &&
|
||||
!string.Equals(node.Lang, expected.Lang, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expected.Kind) &&
|
||||
!string.Equals(node.Kind, expected.Kind, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expected.PurlPattern) &&
|
||||
!MatchesPattern(node.Purl ?? string.Empty, expected.PurlPattern))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HasMatchingEdge(RichGraph graph, ExpectedEdge expected, double minConfidence)
|
||||
{
|
||||
foreach (var edge in graph.Edges)
|
||||
{
|
||||
if (!MatchesPattern(edge.From, expected.From))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!MatchesPattern(edge.To, expected.To))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expected.Kind) &&
|
||||
!string.Equals(edge.Kind, expected.Kind, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (edge.Confidence < minConfidence)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HasMatchingRoot(RichGraph graph, ExpectedRoot expected)
|
||||
{
|
||||
foreach (var root in graph.Roots)
|
||||
{
|
||||
if (!MatchesPattern(root.Id, expected.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expected.Phase) &&
|
||||
!string.Equals(root.Phase, expected.Phase, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<RichGraphNode> FindUnexpectedNodes(RichGraph graph)
|
||||
{
|
||||
var allExpected = _oracle.ExpectedFunctions
|
||||
.Select(f => f.SymbolId)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
foreach (var node in graph.Nodes)
|
||||
{
|
||||
var isExpected = allExpected.Any(pattern => MatchesPattern(node.Id, pattern) || MatchesPattern(node.SymbolId, pattern));
|
||||
if (!isExpected)
|
||||
{
|
||||
yield return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<RichGraphEdge> FindUnexpectedEdges(RichGraph graph)
|
||||
{
|
||||
foreach (var edge in graph.Edges)
|
||||
{
|
||||
var isExpected = _oracle.ExpectedEdges.Any(e =>
|
||||
MatchesPattern(edge.From, e.From) && MatchesPattern(edge.To, e.To));
|
||||
if (!isExpected)
|
||||
{
|
||||
yield return edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matches a value against a pattern supporting '*' wildcards.
|
||||
/// </summary>
|
||||
private static bool MatchesPattern(string value, string pattern)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pattern))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exact match
|
||||
if (!pattern.Contains('*'))
|
||||
{
|
||||
return string.Equals(value, pattern, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
// Convert wildcard pattern to regex
|
||||
var regexPattern = "^" + Regex.Escape(pattern).Replace("\\*", ".*") + "$";
|
||||
return Regex.IsMatch(value, regexPattern, RegexOptions.None, TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
|
||||
private static PatchOracleSummary GenerateSummary(RichGraph graph, List<PatchOracleViolation> violations)
|
||||
{
|
||||
return new PatchOracleSummary(
|
||||
TotalNodes: graph.Nodes.Count,
|
||||
TotalEdges: graph.Edges.Count,
|
||||
TotalRoots: graph.Roots.Count,
|
||||
MissingFunctions: violations.Count(v => v.Type == ViolationType.MissingFunction),
|
||||
MissingEdges: violations.Count(v => v.Type == ViolationType.MissingEdge),
|
||||
MissingRoots: violations.Count(v => v.Type == ViolationType.MissingRoot),
|
||||
ForbiddenFunctionsPresent: violations.Count(v => v.Type == ViolationType.ForbiddenFunctionPresent),
|
||||
ForbiddenEdgesPresent: violations.Count(v => v.Type == ViolationType.ForbiddenEdgePresent),
|
||||
UnexpectedFunctions: violations.Count(v => v.Type == ViolationType.UnexpectedFunction),
|
||||
UnexpectedEdges: violations.Count(v => v.Type == ViolationType.UnexpectedEdge));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of comparing a graph against a patch-oracle.
|
||||
/// </summary>
|
||||
public sealed record PatchOracleResult(
|
||||
string OracleId,
|
||||
string CaseRef,
|
||||
string Variant,
|
||||
bool Success,
|
||||
IReadOnlyList<PatchOracleViolation> Violations,
|
||||
PatchOracleSummary Summary)
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a human-readable report.
|
||||
/// </summary>
|
||||
public string ToReport()
|
||||
{
|
||||
var lines = new List<string>
|
||||
{
|
||||
$"Patch-Oracle Validation Report",
|
||||
$"==============================",
|
||||
$"Oracle: {OracleId}",
|
||||
$"Case: {CaseRef} ({Variant})",
|
||||
$"Status: {(Success ? "PASS" : "FAIL")}",
|
||||
string.Empty,
|
||||
$"Graph Statistics:",
|
||||
$" Nodes: {Summary.TotalNodes}",
|
||||
$" Edges: {Summary.TotalEdges}",
|
||||
$" Roots: {Summary.TotalRoots}",
|
||||
string.Empty
|
||||
};
|
||||
|
||||
if (Violations.Count > 0)
|
||||
{
|
||||
lines.Add($"Violations ({Violations.Count}):");
|
||||
foreach (var v in Violations)
|
||||
{
|
||||
var target = v.To is not null ? $" -> {v.To}" : string.Empty;
|
||||
lines.Add($" [{v.Type}] {v.From}{target}");
|
||||
lines.Add($" Reason: {v.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.Add("No violations found.");
|
||||
}
|
||||
|
||||
return string.Join(Environment.NewLine, lines);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single violation found during oracle comparison.
|
||||
/// </summary>
|
||||
public sealed record PatchOracleViolation(
|
||||
ViolationType Type,
|
||||
string From,
|
||||
string? To,
|
||||
string Message);
|
||||
|
||||
/// <summary>
|
||||
/// Type of oracle violation.
|
||||
/// </summary>
|
||||
public enum ViolationType
|
||||
{
|
||||
MissingFunction,
|
||||
MissingEdge,
|
||||
MissingRoot,
|
||||
ForbiddenFunctionPresent,
|
||||
ForbiddenEdgePresent,
|
||||
UnexpectedFunction,
|
||||
UnexpectedEdge
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summary statistics for oracle comparison.
|
||||
/// </summary>
|
||||
public sealed record PatchOracleSummary(
|
||||
int TotalNodes,
|
||||
int TotalEdges,
|
||||
int TotalRoots,
|
||||
int MissingFunctions,
|
||||
int MissingEdges,
|
||||
int MissingRoots,
|
||||
int ForbiddenFunctionsPresent,
|
||||
int ForbiddenEdgesPresent,
|
||||
int UnexpectedFunctions,
|
||||
int UnexpectedEdges);
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace StellaOps.Reachability.FixtureTests.PatchOracle;
|
||||
|
||||
/// <summary>
|
||||
/// Loads patch-oracle definitions from fixture files.
|
||||
/// </summary>
|
||||
public sealed class PatchOracleLoader
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
AllowTrailingCommas = true
|
||||
};
|
||||
|
||||
private readonly string _fixtureRoot;
|
||||
|
||||
public PatchOracleLoader(string fixtureRoot)
|
||||
{
|
||||
_fixtureRoot = fixtureRoot ?? throw new ArgumentNullException(nameof(fixtureRoot));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the oracle index from INDEX.json.
|
||||
/// </summary>
|
||||
public PatchOracleIndex LoadIndex()
|
||||
{
|
||||
var indexPath = Path.Combine(_fixtureRoot, "INDEX.json");
|
||||
if (!File.Exists(indexPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Patch-oracle INDEX.json not found at {indexPath}");
|
||||
}
|
||||
|
||||
var json = File.ReadAllText(indexPath);
|
||||
return JsonSerializer.Deserialize<PatchOracleIndex>(json, JsonOptions)
|
||||
?? throw new InvalidOperationException("Failed to deserialize patch-oracle index");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads an oracle definition by its ID.
|
||||
/// </summary>
|
||||
public PatchOracleDefinition LoadOracle(string oracleId)
|
||||
{
|
||||
var index = LoadIndex();
|
||||
var entry = index.Oracles
|
||||
.FirstOrDefault(o => string.Equals(o.Id, oracleId, StringComparison.Ordinal))
|
||||
?? throw new KeyNotFoundException($"Oracle '{oracleId}' not found in index");
|
||||
|
||||
return LoadOracleFromPath(entry.Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads an oracle definition from a relative path.
|
||||
/// </summary>
|
||||
public PatchOracleDefinition LoadOracleFromPath(string relativePath)
|
||||
{
|
||||
var fullPath = Path.Combine(_fixtureRoot, relativePath);
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Oracle file not found at {fullPath}");
|
||||
}
|
||||
|
||||
var json = File.ReadAllText(fullPath);
|
||||
return JsonSerializer.Deserialize<PatchOracleDefinition>(json, JsonOptions)
|
||||
?? throw new InvalidOperationException($"Failed to deserialize oracle from {fullPath}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all oracles for a specific case.
|
||||
/// </summary>
|
||||
public IEnumerable<PatchOracleDefinition> LoadOraclesForCase(string caseRef)
|
||||
{
|
||||
var index = LoadIndex();
|
||||
foreach (var entry in index.Oracles.Where(o => string.Equals(o.CaseRef, caseRef, StringComparison.Ordinal)))
|
||||
{
|
||||
yield return LoadOracleFromPath(entry.Path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all available oracles.
|
||||
/// </summary>
|
||||
public IEnumerable<PatchOracleDefinition> LoadAllOracles()
|
||||
{
|
||||
var index = LoadIndex();
|
||||
foreach (var entry in index.Oracles)
|
||||
{
|
||||
yield return LoadOracleFromPath(entry.Path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates all oracle entries without loading full definitions.
|
||||
/// </summary>
|
||||
public IEnumerable<PatchOracleIndexEntry> EnumerateOracles()
|
||||
{
|
||||
var index = LoadIndex();
|
||||
return index.Oracles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the oracle index exists.
|
||||
/// </summary>
|
||||
public bool IndexExists()
|
||||
{
|
||||
return File.Exists(Path.Combine(_fixtureRoot, "INDEX.json"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Reachability.FixtureTests.PatchOracle;
|
||||
|
||||
/// <summary>
|
||||
/// Root model for patch-oracle fixture files.
|
||||
/// </summary>
|
||||
public sealed record PatchOracleDefinition
|
||||
{
|
||||
[JsonPropertyName("schema_version")]
|
||||
public string SchemaVersion { get; init; } = "patch-oracle/v1";
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; init; }
|
||||
|
||||
[JsonPropertyName("case_ref")]
|
||||
public required string CaseRef { get; init; }
|
||||
|
||||
[JsonPropertyName("variant")]
|
||||
public required string Variant { get; init; }
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; init; }
|
||||
|
||||
[JsonPropertyName("expected_functions")]
|
||||
public IReadOnlyList<ExpectedFunction> ExpectedFunctions { get; init; } = Array.Empty<ExpectedFunction>();
|
||||
|
||||
[JsonPropertyName("expected_edges")]
|
||||
public IReadOnlyList<ExpectedEdge> ExpectedEdges { get; init; } = Array.Empty<ExpectedEdge>();
|
||||
|
||||
[JsonPropertyName("expected_roots")]
|
||||
public IReadOnlyList<ExpectedRoot> ExpectedRoots { get; init; } = Array.Empty<ExpectedRoot>();
|
||||
|
||||
[JsonPropertyName("forbidden_functions")]
|
||||
public IReadOnlyList<ExpectedFunction> ForbiddenFunctions { get; init; } = Array.Empty<ExpectedFunction>();
|
||||
|
||||
[JsonPropertyName("forbidden_edges")]
|
||||
public IReadOnlyList<ExpectedEdge> ForbiddenEdges { get; init; } = Array.Empty<ExpectedEdge>();
|
||||
|
||||
[JsonPropertyName("min_confidence")]
|
||||
public double MinConfidence { get; init; } = 0.5;
|
||||
|
||||
[JsonPropertyName("strict_mode")]
|
||||
public bool StrictMode { get; init; } = false;
|
||||
|
||||
[JsonPropertyName("created_at")]
|
||||
public DateTimeOffset? CreatedAt { get; init; }
|
||||
|
||||
[JsonPropertyName("updated_at")]
|
||||
public DateTimeOffset? UpdatedAt { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expected function/node in the graph.
|
||||
/// </summary>
|
||||
public sealed record ExpectedFunction
|
||||
{
|
||||
[JsonPropertyName("symbol_id")]
|
||||
public required string SymbolId { get; init; }
|
||||
|
||||
[JsonPropertyName("lang")]
|
||||
public string? Lang { get; init; }
|
||||
|
||||
[JsonPropertyName("kind")]
|
||||
public string? Kind { get; init; }
|
||||
|
||||
[JsonPropertyName("purl_pattern")]
|
||||
public string? PurlPattern { get; init; }
|
||||
|
||||
[JsonPropertyName("required")]
|
||||
public bool Required { get; init; } = true;
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expected edge in the graph.
|
||||
/// </summary>
|
||||
public sealed record ExpectedEdge
|
||||
{
|
||||
[JsonPropertyName("from")]
|
||||
public required string From { get; init; }
|
||||
|
||||
[JsonPropertyName("to")]
|
||||
public required string To { get; init; }
|
||||
|
||||
[JsonPropertyName("kind")]
|
||||
public string? Kind { get; init; }
|
||||
|
||||
[JsonPropertyName("min_confidence")]
|
||||
public double? MinConfidence { get; init; }
|
||||
|
||||
[JsonPropertyName("required")]
|
||||
public bool Required { get; init; } = true;
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expected root node in the graph.
|
||||
/// </summary>
|
||||
public sealed record ExpectedRoot
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; init; }
|
||||
|
||||
[JsonPropertyName("phase")]
|
||||
public string? Phase { get; init; }
|
||||
|
||||
[JsonPropertyName("required")]
|
||||
public bool Required { get; init; } = true;
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string? Reason { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index entry for an oracle.
|
||||
/// </summary>
|
||||
public sealed record PatchOracleIndexEntry
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; init; }
|
||||
|
||||
[JsonPropertyName("case_ref")]
|
||||
public required string CaseRef { get; init; }
|
||||
|
||||
[JsonPropertyName("variant")]
|
||||
public required string Variant { get; init; }
|
||||
|
||||
[JsonPropertyName("path")]
|
||||
public required string Path { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Root model for patch-oracle INDEX.json.
|
||||
/// </summary>
|
||||
public sealed record PatchOracleIndex
|
||||
{
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; init; } = "1.0";
|
||||
|
||||
[JsonPropertyName("schema")]
|
||||
public string Schema { get; init; } = "patch-oracle/v1";
|
||||
|
||||
[JsonPropertyName("generated_at")]
|
||||
public DateTimeOffset? GeneratedAt { get; init; }
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string? Description { get; init; }
|
||||
|
||||
[JsonPropertyName("oracles")]
|
||||
public IReadOnlyList<PatchOracleIndexEntry> Oracles { get; init; } = Array.Empty<PatchOracleIndexEntry>();
|
||||
}
|
||||
Reference in New Issue
Block a user