Claim-check compaction — a market retires, its holders keep their money — 2026-08-30

Result

A terminal market can now retire past a holder who never comes back, and that holder still gets paid — the same number, to the atom, that redeeming on time would have paid them.

Census row R3 said the opposite: one sleeping holder blocks retirement forever. The zero-supply gate at market_closure_v1.rs:669-681 admits no dust and no write-off, redemption is owner-signed by design, and no Clock is read anywhere on that path — so a lost key held the market, its rent, and every downstream recovery open with no bound. Ember ruled option (c) on it this morning, as a values call rather than a mechanism one: perpetual claim, not perpetual account. Claims do not expire.

What shipped is that ruling. After a release-fixed deadline anybody may open a per-market escrow and then crank each sleeping position through redemption's own payout derivation, paying the resolved collateral into an escrow only that holder can open. The position and its admission record close, the supply they held is retired, and the crank is paid out of rent that was leaving those accounts anyway. The holder returns whenever they like — years later, to a market whose every account is gone — signs with their own key, and is paid out of seven accounts, none of which is the market's.

One ELF changed: claims-sbf. No Core, Trading, Registry or Custody change, no Lean regeneration, no change to Action or CoreState.

The assertion the feature exists for

Compaction does not compute a payout. It calls the holder's own terminal settlement route, with two fields changed — recipient_owner and recipient_token_account — and nothing else.

That is why the two paths cannot diverge, and the campaign proves it as a number rather than as a claim. Both halves run against the same fixture: one where the holder redeems normally, one where they sleep through the deadline and a stranger cranks. Each figure is read off the chain.

a_compacted_claim_check_is_worth_exactly_what_redemption_would_have_paid ... ok

A second author for the payoff function is how a compaction that pays a different number than redemption would have gets built and passes its own tests. The wire enforces the absence of one: the compaction request carries a TerminalSettlementRequestV3 verbatim, decoded by that header's own decoder and by nothing else, so a corrupted byte inside is caught by the terminal decoder rather than by a copy of it.

Measured

routecompute unitsaccounts
OpenClaimCheckEscrow33,76812
CompactPositionToClaimCheck499,80942
RedeemClaimCheck13,3997
CloseClaimCheckEscrow—6

Redemption costs 37× less than the compaction that created it, and touches seven accounts against forty-two. That gap is the design: the payout is resolved once, at compaction, into a fixed-width record, because every input to the payout function — the aggregate, the position, the linked basis record, the composition graph record, the Hoard — is destroyed by the retirement the claim-check exists to permit. A record storing raw per-outcome atoms would be an IOU denominated in a function nobody can evaluate.

The residue, stated exactly

Not nothing — an escrow and a vault survive for as long as anybody is owed, and pretending otherwise would be the kind of claim this project does not make. What is true is that the residue is proportional to unredeemed claims rather than to the market, is width-independent, and is self-liquidating: the last redemption is what allows the escrow's close, after which nothing remains.

An escrow still owing somebody cannot be closed at all (0x5625). That is the ruling working as intended rather than a leak: the claim survives, so the collateral has to be somewhere.

Running it

The whole arc, against the real Claims, Core, Custody, Registry, Resolution and Token-2022 ELFs:

cargo build-sbf --manifest-path programs/dclutch-claims-sbf/Cargo.toml \
  --sbf-out-dir "$SBF_OUT_DIR"

SBF_OUT_DIR=/absolute/sbf cargo test -p dclutch-claims-sbf \
  --test rational_representation_v2_program_test claim_check -- --test-threads=1
test result: ok. 16 passed

$SBF_OUT_DIR needs dclutch_claims_sbf.so, dclutch_core_sbf.so, dclutch_custody_sbf.so, dclutch_registry_sbf.so, dclutch_resolution_proof_sbf.so, dclutch_rational_v2_test_caller_sbf.so and the canonical spl_token_2022.so; tools/gauntlet/claims-extended/run-claims-extended.sh builds all of them. That script also refuses to run at all if any artifact carries an SBF stack-frame-overwrite diagnostic, which is why process_compaction is split across two #[inline(never)] frames.

The one scenario that is the whole feature in a sentence:

SBF_OUT_DIR=/absolute/sbf cargo test -p dclutch-claims-sbf \
  --test rational_representation_v2_program_test \
  claim_check::a_market_retires_a_sleeping_holders_position_and_the_holder_is_still_paid \
  -- --test-threads=1

The contract layer, which needs no ELF and no validator:

cargo test -p dclutch-claims-svm --lib claim_check        # 61 passed
cargo test -p dclutch-claims-sbf --lib claim_check        #  6 passed
cargo test -p dclutch-operator   --lib claim_check        #  5 passed

And the refusal-code gate, which must see the seventeen new codes rather than merely pass:

tools/gauntlet/census/target/release/dclutch-route-census inventory \
  --root . --out /tmp/inventory.json --revision "$(git rev-parse HEAD)" --check-unique
census: 292 refusal codes declared across 130 packages, against 25 registered bands

Whole-file regression, including the 25 scenarios this lane did not write:

test result: ok. 41 passed; 0 failed

What the tests refuse to let a later edit do

Debt, named

Findings this lane returned to the wave

Provenance

Design: docs/design/CLAIM_CHECK_COMPACTION_V1.md, including §15 (the six amendments implementation earned) and §16 (what shipped, reconciled against what was planned). Ruling: docs/ledger/WAVE_2026-08-26_to_2026-09-02.md:1183-1186. Thirteen commits, a2ad25ed through dff38069.