v2.2.0
v2.2.0 — Healing proven live, claims that can never get stuck, create where you are
Compatibility: SOFT — no wire-format changes, no lockstep. Every flow works
across v2.0.x / v2.1 / v2.2 in any mix. The new guarantees activate on the
device that updates: backups gain field-healing powers on their own update,
claim retries self-heal on the claimer's update, and pooled envelopes include
backups once the PUBLISHING device is on v2.2. No funds at risk in mixed
versions — older devices simply behave like v2.1.
Healing substitution, proven end-to-end in the field (the ₿121 trade)
- v2.1.0's blind healing was run live: a backup arbiter healed a disputed
trade that expired while the assigned arbiter was absent — refund voted,
resolve published, sats claimed back to the locker's wallet through NWC.
The live run exposed four stacked gaps, all fixed here: - Healing now judges by the CLOCK, not the status flag. An expired trade is
healable the second its deadline passes — previously the backup's client
waited for a status flip that only an arriving event could cause, so
"expiry" did nothing until someone else acted. - Pooled-trade envelopes now include the backup arbiters. VOTE / CLAIM /
RESOLVE / release events on pool-share locks are encrypted to the priority
order, not just the three participants — backups see live tallies and
settled outcomes instead of "0 votes on both sides". (Events published by
pre-2.2 devices remain sealed to participants; those partial backup views
are cosmetic and age out as devices update.) - Healable expired trades now appear in the arbiter NEEDS queue ("expired
unresolved — open it to heal"), instead of only LOCKED disputes counting. - RESOLVE starvation fixed: a trade whose votes already decided the outcome
could sit unresolved forever because the expired-trade path never tried to
publish the resolve. Loading an expired trade — and the background sweep —
now publishes any RESOLVE the chain already justifies. - Trade-room framing tells the truth mid-heal: an expired-but-healable trade
reads "EXPIRED · HEALABLE — an arbiter can still heal it", and once a heal
vote lands it reads "HEALING — refund confirming" instead of the old
"CLOSED — no longer active" scare.
Claims can never get stuck (the sm_mq1cmq6p_a2arwi0x ₿17 incident)
- Field-found on a slow connection: a first claim's redeem landed AFTER the
60s confirm window gave up. Every retry then re-measured balance growth
from a baseline that already included the landed sats — growth that can
never be observed again — looping "still arriving" forever while the sats
sat invisibly in the wallet (below every recovery surface's dust line). - Settlement is now provable two ways: balance GROWTH (as before), or the
absolute balance COVERING the promised payout. If the wallet can pay the
invoice right now, the claim pays out — retries converge instead of
looping. One Claim tap after updating pays out a previously-stuck trade. - "Notes already consumed" throws get the same rescue: consumed notes plus a
covering balance means a previous attempt already credited this wallet, so
the payout proceeds instead of dead-ending. Consumed plus an empty wallet
still fails honestly — no false hope. - Slow links get a fair window: 90s confirm (up from 60s), failed balance
reads extend the deadline instead of eating it (a read that errors proves
nothing about non-arrival), hard-capped at 3× so the loop always ends. - Cover-path payouts send the money FIRST and publish COMPLETE only after it
lands — if the payout leg fails, the trade stays claimable and the modal
says exactly that ("close this and tap Claim again — retries with a fresh
invoice") instead of pointing small amounts at a recovery surface that
ignores anything under the dust line.
Create where you are (the stale-home trap)
- The Create wizard now stamps the community your header actually shows —
what you see is what you publish. Previously it silently used the home
picked at sign-in (the only place that could ever change it), so a stale
home meant listings went out with the wrong country, wrong default
currency, and the wrong community arbiter pool while every pixel on screen
said otherwise. - When the community you're publishing in differs from your persisted home,
the listing line grows a one-tap SET AS HOME → affordance — fix the stale
home at the exact moment the mismatch is visible, no trip back to the
sign-in screen. Home keeps deciding where Chama signs you in; nothing else
about boot routing changed.
Your "Chama ready" buzz is back (Android)
- The boot haptic regressed because it rode the WebView's navigator.vibrate,
which Android gates behind a fresh user gesture — so on auto-login (no tap
at all) it could never fire, and on tap-to-connect the relay handshake
usually outlived the ~5s activation window. Either way the buzz got eaten. - Haptics now route through the native @capacitor/haptics plugin on-device,
which calls the system vibrator straight through the bridge with no
activation requirement. The exact patterns are preserved — the connected
double-buzz, the celebratory claim, every tap — replayed faithfully pulse
for pulse. Web stays on navigator.vibrate (which already honours patterns).
Numbers
- 2,187 tests green, including new suites pinning the cover-settlement
verdicts (rescue, timeout-cover, no-cover honesty, pay-then-complete
ordering, stash preservation) and the slow-link confirm window.