polyc-eventlog 2026.9.0

Append-only conversation event log on a commonware-storage journal.
//! Errors surfaced by the event log.

/// Failures from opening, appending to, or replaying an [`crate::EventLog`].
///
/// This is a thin wrapper over the underlying
/// [`commonware_storage::journal::Error`]: every fallible event-log operation
/// delegates to the journal primitive, so the storage error is the only failure
/// mode and is preserved verbatim as the [`Self::Journal`] source.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum EventLogError {
    /// The underlying commonware-storage journal returned an error while
    /// opening, appending, committing, or replaying.
    #[error("event log journal error: {0}")]
    Journal(#[from] commonware_storage::journal::Error),
    /// The durable event-count checkpoint ([`crate::checkpoint`]) failed to
    /// open, read, or sync.
    #[error("event log checkpoint error: {0}")]
    Checkpoint(#[from] commonware_storage::metadata::Error),
    /// A partition's tamper-evidence tree could not be rebuilt, or its root
    /// could not be signed.
    ///
    /// A rewrite reports this rather than proceeding: every root the partition
    /// carried was signed over content the rewrite changes, so a rewrite that
    /// cannot sign a fresh one would leave a partition whose signed history
    /// does not describe what it holds.
    #[error("event log integrity error: {0}")]
    Integrity(#[from] crate::IntegrityError),
    /// [`crate::EventLog::reclaim`] was asked to remove storage that still
    /// holds events.
    ///
    /// Reclaim is Commonware's final-teardown removal, which is not crash
    /// safe. It is sound only over a journal a
    /// [`crate::EventLog::reset`] already emptied durably, so a caller that
    /// skipped the reset is refused rather than served an interrupted removal
    /// that could leave a short history.
    #[error("event log reclaim refused: the journal still holds {events} event(s); reset it first")]
    ReclaimNotEmpty {
        /// How many events the journal still held.
        events: u64,
    },
    /// An erasure reported success while durable storage for the partition
    /// survived it.
    ///
    /// Commonware's runtime maps EVERY `remove_dir_all` failure to
    /// `PartitionMissing`, and both `Partition::remove_all` and
    /// `Metadata::destroy` treat that as success. A real I/O failure part-way
    /// through a removal is therefore indistinguishable, inside Commonware,
    /// from storage that was already gone — so a reclaim can answer `Ok` over
    /// a remnant it did not remove.
    ///
    /// The erase path checks the volume afterwards rather than trusting that
    /// answer. Without the check the caller receives a completion it can act
    /// on, writes its receipt, and leaves a remnant no workflow will come back
    /// for — which every later read refuses as damage.
    #[error("event log erasure incomplete: durable storage survived the removal")]
    EraseIncomplete,
    /// A requested repair transformation did not name exactly one survivor.
    #[error("event log repair replacement error: {0}")]
    RepairReplacement(String),
    /// A repair refused a partition whose signed roots contradict the content
    /// they cover.
    ///
    /// Repair deletes every root a partition holds and signs one fresh root
    /// under the trusted key. So it may run only where the verification
    /// failure is a claim about content that is gone. Here the surviving
    /// content is not what a trusted key attested, and re-rooting would sign
    /// that contradiction rather than report it.
    ///
    /// Distinct from [`Self::Integrity`], which reports a partition that does
    /// not verify. This reports a partition this process declines to change.
    #[error(
        "repair refused: a trusted signed root contradicts the content it covers, so re-rooting \
         would sign the contradiction — preserve this partition and investigate (see \
         docs/operations/eventlog-disaster-recovery.md)"
    )]
    RepairRefused,
}