The rule

A report cannot exist while one lamport has no destination.

Not a policy. The CLOSED arm of a basket requires a seal, the seal carries a symbol this codebase declares and never exports, and the only function that returns one checks the arithmetic first.

§1   Four destinations, four different proofs

HOLDERSmust carry evidencethe transaction that moved it

Money that reached people left the wallet, so there is a signature. Naming it is the whole difference between a distribution and a promise — and the figure that has no signature behind it is, without exception, a database row.

COSTSmust carry evidencethe transaction that moved it

A cost is money that left. If it left, it left in something anybody can open. A cost line with no transaction is indistinguishable from a subtraction somebody typed.

RESERVEDmust carry untila date

It has not moved, so there is no signature to give — which is exactly why this arm demands something else instead. Without a date, “reserved” is a nicer word for missing, and it can stay that way for years.

RESIDUEmust carry nextActionwhat happens to it next

You may have a remainder. Rounding is real, sweeps run late, a swap leaves dust. What you may not do is name it a remainder and walk away — which is the precise shape of the 207.74 SOL that started this project.

§2   The seal

declare const CLOSED: unique symbol;          // declared, never exported

export type Closed = {
  readonly [CLOSED]: true;
  readonly received: bigint;
  readonly assigned: bigint;
};

type Basket =
  | { state: "CLOSED"; received; allocations; closed: Closed }
  | ...

An object literal cannot supply a key it cannot write down. Nothing outside lib/sportula.ts can name that symbol, so nothing outside can build a seal — and close() only returns one when the allocations sum exactly to what arrived, compared as bigint. One lamport out is one lamport out.

It also refuses negative allocations before it adds anything up. Without that, two wrong numbers cancel and the books balance without ever touching the type system — the one way this rule could be satisfied dishonestly.

§3   Three states, and the middle one is not an error

OPENmoney has arrived and is still being assigned.
UNBALANCEDthis does not add up, and here is the difference.
CLOSEDevery lamport that arrived has a destination.

closed — 1 SOL, all of it assigned

to holders0.9residue0.1

unbalanced — the residue was never named

to holders0.9no destination0.1

UNBALANCED is displayable on purpose. A system that could only represent balanced books would not stop money going unaccounted for — it would only stop anyone seeing it, and the same figure would move somewhere quieter. What is removed here is not the imbalance. It is the ability not to mention one.