Files
git.stella-ops.org/src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.DeltaSig/HybridDiffContracts.cs
2026-02-17 00:51:35 +02:00

436 lines
11 KiB
C#

// Copyright (c) StellaOps. All rights reserved.
// Licensed under BUSL-1.1. See LICENSE in the project root.
using System.Text.Json.Serialization;
namespace StellaOps.BinaryIndex.DeltaSig;
/// <summary>
/// Source file pair used to compute semantic edit scripts.
/// </summary>
public sealed record SourceFileDiff
{
/// <summary>
/// Source file path (repository-relative).
/// </summary>
public required string Path { get; init; }
/// <summary>
/// Previous file content.
/// </summary>
public string? BeforeContent { get; init; }
/// <summary>
/// Current file content.
/// </summary>
public string? AfterContent { get; init; }
}
/// <summary>
/// Source line span.
/// </summary>
public sealed record SourceSpan
{
/// <summary>
/// 1-based start line.
/// </summary>
public required int StartLine { get; init; }
/// <summary>
/// 1-based end line.
/// </summary>
public required int EndLine { get; init; }
}
/// <summary>
/// Deterministic semantic edit script.
/// </summary>
public sealed record SemanticEditScript
{
/// <summary>
/// Schema version.
/// </summary>
public string SchemaVersion { get; init; } = "1.0.0";
/// <summary>
/// Digest of the full source tree diff input.
/// </summary>
public required string SourceTreeDigest { get; init; }
/// <summary>
/// Deterministic ordered edits.
/// </summary>
public required IReadOnlyList<SemanticEdit> Edits { get; init; }
}
/// <summary>
/// Single semantic edit entry.
/// </summary>
public sealed record SemanticEdit
{
/// <summary>
/// Stable digest-derived ID for dedupe and audit references.
/// </summary>
public required string StableId { get; init; }
/// <summary>
/// Edit type: add, remove, move, update, rename.
/// </summary>
public required string EditType { get; init; }
/// <summary>
/// Node kind: file, method, class, field, import, statement.
/// </summary>
public required string NodeKind { get; init; }
/// <summary>
/// Deterministic node path.
/// </summary>
public required string NodePath { get; init; }
/// <summary>
/// Symbol anchor used to link to binary symbols.
/// </summary>
public required string Anchor { get; init; }
/// <summary>
/// Pre-change source span.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public SourceSpan? PreSpan { get; init; }
/// <summary>
/// Post-change source span.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public SourceSpan? PostSpan { get; init; }
/// <summary>
/// Pre-change digest.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? PreDigest { get; init; }
/// <summary>
/// Post-change digest.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? PostDigest { get; init; }
}
/// <summary>
/// Build-stage symbol map linked to build identity.
/// </summary>
public sealed record SymbolMap
{
/// <summary>
/// Schema version.
/// </summary>
public string SchemaVersion { get; init; } = "1.0.0";
/// <summary>
/// Build ID (ELF build-id, PDB GUID, or equivalent).
/// </summary>
public required string BuildId { get; init; }
/// <summary>
/// Optional binary digest for traceability.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? BinaryDigest { get; init; }
/// <summary>
/// Address derivation source (manifest, synthetic-signature).
/// </summary>
public string AddressSource { get; init; } = "manifest";
/// <summary>
/// Ordered symbol entries.
/// </summary>
public required IReadOnlyList<SymbolMapEntry> Symbols { get; init; }
}
/// <summary>
/// Symbol map entry.
/// </summary>
public sealed record SymbolMapEntry
{
/// <summary>
/// Symbol name.
/// </summary>
public required string Name { get; init; }
/// <summary>
/// Symbol kind: function, object, section.
/// </summary>
public string Kind { get; init; } = "function";
/// <summary>
/// Start address.
/// </summary>
public required ulong AddressStart { get; init; }
/// <summary>
/// End address.
/// </summary>
public required ulong AddressEnd { get; init; }
/// <summary>
/// Containing section.
/// </summary>
public string Section { get; init; } = ".text";
/// <summary>
/// Source ranges for this symbol.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IReadOnlyList<SourceRange>? SourceRanges { get; init; }
/// <summary>
/// Symbol size derived from address range.
/// </summary>
[JsonIgnore]
public long Size => checked((long)(AddressEnd >= AddressStart ? AddressEnd - AddressStart + 1UL : 0UL));
}
/// <summary>
/// Source range metadata.
/// </summary>
public sealed record SourceRange
{
/// <summary>
/// Source file path.
/// </summary>
public required string File { get; init; }
/// <summary>
/// 1-based start line.
/// </summary>
public required int LineStart { get; init; }
/// <summary>
/// 1-based end line.
/// </summary>
public required int LineEnd { get; init; }
}
/// <summary>
/// Join artifact linking semantic edits to symbol changes.
/// </summary>
public sealed record SymbolPatchPlan
{
/// <summary>
/// Schema version.
/// </summary>
public string SchemaVersion { get; init; } = "1.0.0";
/// <summary>
/// Build ID before patch.
/// </summary>
public required string BuildIdBefore { get; init; }
/// <summary>
/// Build ID after patch.
/// </summary>
public required string BuildIdAfter { get; init; }
/// <summary>
/// Semantic script digest.
/// </summary>
public required string EditsDigest { get; init; }
/// <summary>
/// Old symbol map digest.
/// </summary>
public required string SymbolMapDigestBefore { get; init; }
/// <summary>
/// New symbol map digest.
/// </summary>
public required string SymbolMapDigestAfter { get; init; }
/// <summary>
/// Ordered symbol changes.
/// </summary>
public required IReadOnlyList<SymbolPatchChange> Changes { get; init; }
}
/// <summary>
/// Single symbol patch plan entry.
/// </summary>
public sealed record SymbolPatchChange
{
/// <summary>
/// Symbol name.
/// </summary>
public required string Symbol { get; init; }
/// <summary>
/// Change type: added, removed, modified, moved.
/// </summary>
public required string ChangeType { get; init; }
/// <summary>
/// Linked source anchors.
/// </summary>
public required IReadOnlyList<string> AstAnchors { get; init; }
/// <summary>
/// Hash before.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? PreHash { get; init; }
/// <summary>
/// Hash after.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? PostHash { get; init; }
/// <summary>
/// Delta reference digest.
/// </summary>
public required string DeltaRef { get; init; }
}
/// <summary>
/// Normalized patch manifest for per-symbol deltas.
/// </summary>
public sealed record PatchManifest
{
/// <summary>
/// Schema version.
/// </summary>
public string SchemaVersion { get; init; } = "1.0.0";
/// <summary>
/// Build ID.
/// </summary>
public required string BuildId { get; init; }
/// <summary>
/// Normalization recipe identifier.
/// </summary>
public required string NormalizationRecipeId { get; init; }
/// <summary>
/// Ordered patch entries.
/// </summary>
public required IReadOnlyList<SymbolPatchArtifact> Patches { get; init; }
/// <summary>
/// Total delta bytes across symbols.
/// </summary>
[JsonIgnore]
public long TotalDeltaBytes => Patches.Sum(p => p.DeltaSizeBytes);
}
/// <summary>
/// Per-symbol patch artifact.
/// </summary>
public sealed record SymbolPatchArtifact
{
/// <summary>
/// Symbol name.
/// </summary>
public required string Symbol { get; init; }
/// <summary>
/// Address range in hex format.
/// </summary>
public required string AddressRange { get; init; }
/// <summary>
/// Digest of patch payload.
/// </summary>
public required string DeltaDigest { get; init; }
/// <summary>
/// Pre-patch metrics.
/// </summary>
public required PatchSizeHash Pre { get; init; }
/// <summary>
/// Post-patch metrics.
/// </summary>
public required PatchSizeHash Post { get; init; }
/// <summary>
/// Absolute byte delta.
/// </summary>
[JsonIgnore]
public long DeltaSizeBytes => Math.Abs(Post.Size - Pre.Size);
}
/// <summary>
/// Size/hash tuple.
/// </summary>
public sealed record PatchSizeHash
{
/// <summary>
/// Size in bytes.
/// </summary>
public required long Size { get; init; }
/// <summary>
/// Hash digest.
/// </summary>
public required string Hash { get; init; }
}
/// <summary>
/// Full hybrid diff evidence bundle.
/// </summary>
public sealed record HybridDiffEvidence
{
/// <summary>
/// Semantic edit script.
/// </summary>
public required SemanticEditScript SemanticEditScript { get; init; }
/// <summary>
/// Old symbol map.
/// </summary>
public required SymbolMap OldSymbolMap { get; init; }
/// <summary>
/// New symbol map.
/// </summary>
public required SymbolMap NewSymbolMap { get; init; }
/// <summary>
/// Symbol patch plan.
/// </summary>
public required SymbolPatchPlan SymbolPatchPlan { get; init; }
/// <summary>
/// Normalized patch manifest.
/// </summary>
public required PatchManifest PatchManifest { get; init; }
/// <summary>
/// Semantic edit script digest.
/// </summary>
public required string SemanticEditScriptDigest { get; init; }
/// <summary>
/// Old symbol map digest.
/// </summary>
public required string OldSymbolMapDigest { get; init; }
/// <summary>
/// New symbol map digest.
/// </summary>
public required string NewSymbolMapDigest { get; init; }
/// <summary>
/// Symbol patch plan digest.
/// </summary>
public required string SymbolPatchPlanDigest { get; init; }
/// <summary>
/// Patch manifest digest.
/// </summary>
public required string PatchManifestDigest { get; init; }
}