Android half of ios#16. `Sent.sealed` already reported each send's
outcome, but nothing accumulated, so nobody could answer "how often does
this happen to me".
🔴 The copy distinguishes sender from content, with tests asserting it.
An unsealed send is still end-to-end encrypted; what the relay learns is
who sent it. Somebody who reads "sealed" as "encrypted" makes the wrong
decision in both directions.
⚠️ Aggregate only. Per-contact counts would be more useful and would also
build a record of who this account talks to and how often — the precise
thing sealing exists to keep off a server.
Refusing is offered and off by default: it turns a metadata leak into an
undelivered message, which for most people is the worse outcome. It has
its own exception type so the refusal can explain itself.
⚠️ Refusing spends nothing. `sealed` is false exactly when neither a
capability nor a key was found, so `takePeerToken` returned null and
there is no token to hand back — my first cut had a line handing one
back, which was unreachable.
⚠️ The policy and the counter are injected as lambdas, since `client` and
the engine have no Android dependency, and both are wired in one helper.
Two sign-in paths build a session, and only one of them applying the
policy is how a setting silently stops working on half the app.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>