LedgerConflict

An integrity fault EntitlementLedger.validate derives from merged state.

Conflicts are surfaced, never resolved by timestamp (design §4.6): every replica folds the same merged state into the same, deterministically-sorted report. The affected lineage contributes no spendable holdings (quarantine is transitive down the path), and nothing here is ever silently resolved by arrival order or a clock.

validate() is an eventually-consistent diagnostic, not a safety gate. Safety comes from the local holdings check each mutator runs on the actor's own complete state — never from a conflict report. Under partial delivery a report may transiently list a conflict that a later anti-entropy round dissolves (see EntitlementLedger); consumers must not hard-gate on validate().isEmpty() while rebalancing is in flight.

Comparable so a report is emitted in one canonical order on every peer — the ordering is derived from the typed identities below (no stringified keys).

Inheritors

Types

Link copied to clipboard

A Lifecycle.RETIRED edge across which entitlement nonetheless still stands — outstanding(e) != 0 (design §5.1, §10.10). EntitlementLedger.retire refuses to retire an edge until it has fully drained, so on a causally-complete state this means a late delegation crossed a generation the cluster had already retired: a replica acting on stale Lifecycle.ACTIVE state delegated down an edge another replica had already close-drained-retired. The max-register makes RETIRED dominate the merge (closure dominance); this report surfaces the late crossing rather than resolving it by arrival order, and the stranded entitlement is reconciled by the control plane.

Link copied to clipboard
data class ConservationViolation(val leafSpentTotal: Long, val mintedTotal: Long) : LedgerConflict

The global supply backstop: total service charged has exceeded total supply ever minted — Σ_e effLeafSpent(e) > Σ mintedTotal (design §10.1 conservation, §10.12).

Link copied to clipboard
data class DualActiveInbound(val group: GroupId) : LedgerConflict

A group with two or more live inbound generations — two inbound edges that are each Lifecycle.ACTIVE or Lifecycle.CLOSING (a still-draining closing edge counts; it can still carry entitlement). This is the topology fork the design forbids resolving by last-writer-wins on a parent pointer (§5.2, §10.11). It arises when two replicas concurrently attach a different inbound edge for the same child — e.g. one activates e2 while another has e1 active or closing; the lifecycle max-register keeps both live, so every replica folds the merged state into the same report rather than silently picking a winner. The child's whole lineage is quarantined — EntitlementLedger.holdings returns zero at or below it — and no new entitlement may be delegated across either contested edge (EntitlementLedger.delegate returns null). This predicate is exactly the one EntitlementLedger quarantines on, so quarantine and report always coincide (§10.11).

Link copied to clipboard
data class LineageCycle(val group: GroupId) : LedgerConflict

A group that lies on a topology cycle — walking its live inbound edges root-ward returns to the group itself instead of reaching a root. Like DualActiveInbound this is a fork the merge deliberately retains rather than resolving by arrival order (§5.2): two replicas each attached an inbound edge, and together the records close a loop. The whole cycle is quarantined — EntitlementLedger.holdings is zero at every group on it and at every group below it — so this report is what makes that quarantine visible (§10.11: quarantine ⟺ explicit report).

Link copied to clipboard

An edge whose effective spend has gone negative — leafSpent + relocIn − relocOut < 0 on either the leaf or the roll-up family (issue #1693; relocation design §5.3's lower bound, §12.5).

Link copied to clipboard

Transfer rows the topology has moved out from under (issue #2366) — a PathKey no group's live lineage reads any more, still carrying a peer-to-peer hand-off that EntitlementLedger.holdings therefore no longer counts.

Link copied to clipboard
data class PerEdgeSafety(val edge: AttachmentId) : LedgerConflict

An edge whose aggregate charged-plus-returned exceeds what was ever issued down it: leafSpent(e) + rollupSpent(e) + returned(e) > issued(e). Checked sum-wise on aggregate values, never per slot — a peer may legitimately return entitlement it received by transfer, so a per-slot returned > issued is fine; only the edge total crossing issued is a fault (design §4.6).

Link copied to clipboard
data class PersistentNegativeHoldings(val group: GroupId, val replica: ReplicaId) : LedgerConflict

A (group, replica) whose derived EntitlementLedger.holdings is negative — the real overspend net (design §4.6): a debit beyond a peer's pocket that nonetheless stays within the edge sum passes PerEdgeSafety yet strands holdings persistently negative. On a fully-delivered state this is a genuine overspend; under partial delivery of a multi-hop transfer-funded charge it may surface transiently and self-heal on anti-entropy.

Link copied to clipboard

Two distinct immutable AttachmentRecords under one AttachmentId — a topology fork the merge deliberately retained rather than resolving by last-writer-wins on a parent pointer (design §5.2). The whole lineage is quarantined: EntitlementLedger.holdings returns zero for any group at or below the divergent edge.

Properties

Link copied to clipboard
abstract val order: Int

The stable ordering rank of this conflict kind (kinds are grouped in the sorted report).

Functions

Link copied to clipboard
open operator override fun compareTo(other: LedgerConflict): Int