using System.Text.Json.Nodes; namespace StellaOps.Findings.Ledger.Domain; public sealed record LedgerEventDraft( string TenantId, Guid ChainId, long SequenceNumber, Guid EventId, string EventType, string PolicyVersion, string FindingId, string ArtifactId, Guid? SourceRunId, string ActorId, string ActorType, DateTimeOffset OccurredAt, DateTimeOffset RecordedAt, JsonObject Payload, JsonObject CanonicalEnvelope, string? ProvidedPreviousHash, string? EvidenceBundleReference = null); public sealed record LedgerEventRecord( string TenantId, Guid ChainId, long SequenceNumber, Guid EventId, string EventType, string PolicyVersion, string FindingId, string ArtifactId, Guid? SourceRunId, string ActorId, string ActorType, DateTimeOffset OccurredAt, DateTimeOffset RecordedAt, JsonObject EventBody, string EventHash, string PreviousHash, string MerkleLeafHash, string CanonicalJson, string? EvidenceBundleReference = null); public sealed record LedgerChainHead( long SequenceNumber, string EventHash, DateTimeOffset RecordedAt); public enum LedgerWriteStatus { Success, Idempotent, ValidationFailed, Conflict } public sealed record LedgerWriteResult( LedgerWriteStatus Status, LedgerEventRecord? Record, IReadOnlyList Errors, LedgerEventRecord? ExistingRecord, string? ConflictCode) { public static LedgerWriteResult ValidationFailed(params string[] errors) => new(LedgerWriteStatus.ValidationFailed, null, errors, null, null); public static LedgerWriteResult Conflict(string code, params string[] errors) => new(LedgerWriteStatus.Conflict, null, errors, null, code); public static LedgerWriteResult Idempotent(LedgerEventRecord record) => new(LedgerWriteStatus.Idempotent, record, Array.Empty(), record, null); public static LedgerWriteResult Success(LedgerEventRecord record) => new(LedgerWriteStatus.Success, record, Array.Empty(), null, null); } public sealed class LedgerDuplicateEventException : Exception { public LedgerDuplicateEventException(Guid eventId, Exception innerException) : base($"Ledger event {eventId} already exists.", innerException) { EventId = eventId; } public Guid EventId { get; } }