OrphanedTransferPath
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.
transfers is keyed by PathKey.of(edge) — the generation's id, not the child group. So when a group's inbound generation is replaced and the rows do not travel with it, the recipient's credit and the donor's debit drop out of the derivation at once, and the donor silently recovers what it gave away.
A EntitlementLedger.relocationPatch carry moves them (#2366) — cancelling the dead key with transferRelocOut and re-opening the live one with transferRelocIn — so a move that completes leaves nothing here to report. What still reaches this report is a generation replaced without one: a plain reshape with no Reconcile behind it, a refused move — including the refusal for a carried hand-off whose donor left the roster before it could ack — an ack that declared no row (the shape a pre-#2377 QuiesceAck had), or rows keyed on a generation this ledger has never seen.
Candidates come from transfers and transferRelocIn
A carry never writes the donor-owned base slot at the live key (#1691) — it lands in transferRelocIn and cancels the dead key with transferRelocOut. So one move on, a key whose entire credit arrived by carry appears in no transfers entry at all, and an enumeration over the base matrix could not reach it however loud the abandonment. That is the second hop of the same defect: carol → alice → bob, carried once onto a fresh generation, and then abandoned there when the next move cannot enumerate the departed alice. transferRelocOut's keys are deliberately not candidates — that matrix only ever cancels, so a key it alone names holds no credit to strand and would contribute only a negative-effRow transient under partial delivery.
Why this needs its own report
Every other check here is structurally incapable of seeing it:
Conservation is blind by construction.
Σ_r transferNet(k, r) = 0for every key, identically — so abandoning a key's rows (or halving them, or double-moving them) is sum-preserving.mintedTotal = Σ holdings + Σ effLeafSpentstill holds exactly; only the owner changed.PersistentNegativeHoldings catches only the loud half. The recipient lands on
0, not below, so nothing goes negative unless the recipient also spent or released across the dead generation.The same asymmetry is why
EntitlementLedger.relocationPatch'sn < 0precondition misses it: a recipient who merely holds transferred credit has no counter slot on the edge at all and is absent from every per-slot enumeration. (It is also why the fence enumerates transfer donors explicitly — seeEntitlementLedger.baseFinalsOn.)
What it takes to fire — all three, together
The key is no longer read. Its edge's child group has a live lineage whose final key is a different one.
The live key does not already cover the rows. Some
(donor, recipient)effective magnitude at this key exceeds the same pair's at the group's live key. Effective on both sides — base ± relocation — because that is whatEntitlementLedger.holdingsreads.This is a magnitude test, and against a base row it is only a necessary condition for abandonment:
EntitlementLedger.transferaccumulates onto the very same(path, donor, recipient)slot, so "the pair transferred at least as much again at the live key" is byte-identical to a carry. The consequence is a real, permanent blind spot — a later ordinary transfer between those two peers at the live key masks a report that had been firing, and rows are grow-only, so the live cumulative never falls back to unmask it.A real EntitlementLedger.relocationPatch carry clears the report through this very clause — it never reaches clause 3, and saying otherwise would have retired the blind spot on an argument about a clause that does not run. The carry cancels the dead key with
transferRelocOut, so every(donor, recipient)effective magnitude there is0by construction and0 ≤ effRow(livePath, …)holds unconditionally: the comparison short-circuits here and returns before the consequence test is asked.That is still not the coincidence this clause is otherwise vulnerable to, and the difference is what makes the retirement sound. The blind spot is that a base pair total at the live key can match the dead one by accident — an unrelated later transfer between the same two peers — so a covered comparison says nothing about whether a carry happened. A cancelled dead side is the opposite:
transferRelocOutis written by nothing but a carry, soeffRow(deadPath) == 0is provenance the lattice carries, read through a magnitude comparison. The blind spot survives only for the base-row coincidence, which no code path produces.It is consequential. Some party to those rows still has a non-zero balance stranded on the dead generation —
netInflow + transferNet − effLeafSpent ≠ 0, the inbound half of EntitlementLedger.holdings evaluated where it is no longer evaluated. Without this clause every honestly drained-and-retired generation that ever carried a hand-off would be reported forever, since the rows are grow-only and its books have already closed at zero.
One arm fires on two clauses, not three. Rows keyed on a generation this ledger does not know are unreadable by construction: clause 1 has no liveness question left to ask and clause 2 has no live key to compare against. That arm keeps the half of clause 3 that survives without an edge — some party's net at the key is non-zero — so that it agrees with the three-clause arm about rows that merely cancel, rather than reporting there what the main arm deliberately does not.
Deliberately silent where the group has no live lineage at all: that is the normal window of an honest reshape (old generation retired, new one not yet active) and the standing exception to §10.11's quarantine ⟺ report correspondence, shared with EntitlementLedger.holdings. A quarantined or divergent lineage is likewise left to RecordDivergence / DualActiveInbound / LineageCycle rather than voiced twice.
Like every report here it is a diagnostic, not a safety gate.