aboutsummaryrefslogtreecommitdiffstats
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
...
* Communities no longer decrypt on the home screen: init built and subscribed ↵Adam Soltys2026-09-031-17/+37
| | | | | | | every community room up front (500 control + 500 guestbook + channels each — the bulk of the ~560ms of boot secp256k1 behind the carousel stutter). Now boot builds rooms from the decrypted local cache ONLY (subscribe:false), so the unread dot still reads last-known state, and the live backfill's verify/decrypt waits until the user actually opens Chat (homeView/room view subscribe then). ensureRoom split into cache-build + subscribeRoom Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The exit-depth advisory stops pricing itself in the render path: it decoded ↵Adam Soltys2026-09-031-21/+39
| | | | | | | every spendable coin and built the exit txs (heavy secp256k1) synchronously on each render, and the cold first compute after a refresh landed right on the carousel drag. Now the render only reads the cached answer; the recompute runs in requestIdleCallback (keyed by coin set + feerate, so it fires only when something changed) and re-renders when done — the EC never competes with input Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Ark reconcile signs in time-sliced batches: it built a status query (schnorr ↵Adam Soltys2026-09-031-3/+12
| | | | | | | sign + vtxo decode, real secp256k1) for every spendable coin in one synchronous map — a ~100ms+ EC burst on boot, the local work blocking a carousel drag right after a refresh (community/DM wraps arrive over the network later; this fires immediately on init). The requests now kick off in ≤6ms slices that yield; network calls still overlap Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The post-refresh carousel stutter, root-caused and fixed: a CPU profile of ↵Adam Soltys2026-09-031-11/+41
| | | | | | | the exact cold first-drag window showed ~700ms of secp256k1, and it was the COMMUNITY room's gift-wrap decryption — a relay backlog of 500+ wraps decrypted inline in the subscription callbacks, one synchronous EC burst on boot (the DM inbox already yielded; community rooms didn't). Now their decrypt work is time-sliced through the same yield-every-8ms queue, so it spreads over a few frames and the wallet stays responsive. Measured: first cold flick dropped 6 frames before, and this was the dominant cost Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Fix the switch that scrollend broke, and land the actual stutter fix. ↵Adam Soltys2026-09-031-21/+15
| | | | | | | scrollend doesn't fire reliably for programmatic scrolls, so relying on it left the card visually switched but the account state stale. Back to the onScroll debounce — but the real mid-glide stall was never the debounce (it trails the glide's own scroll events and fires after motion stops); it was an EXPLICIT 150ms settle timer in the drag release firing mid-glide. That's gone, so the switch render lands only once the card is at rest. scrollend kept as a bonus early trigger where it does fire Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The carousel's stutter was the switch render landing mid-glide: a one-frame ↵Adam Soltys2026-09-031-6/+16
| | | | | | | stall showed up ~150ms into a slide — exactly when the settle timer fired the account switch and rebuilt the history list while the card was still moving. The switch now fires on scrollend (motion truly at rest, card already in place), so its render is invisible; the debounce timers survive only as long fallbacks for browsers without scrollend Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The DM backfill yields to the main thread: decrypting a gift-wrap backlog ↵Adam Soltys2026-09-031-0/+10
| | | | | | | (NIP-44 ECDH, ~500ms of secp256k1 in a boot profile — the last of the carousel jank) ran as one uninterrupted loop. Each synchronous run is now capped to ~8ms, one frame's budget, then yields so input and paint get a turn; the backfill just spreads over a few extra frames instead of freezing them Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Ark key derivation gets memoized — the boot CPU hog. A profile of the ↵Adam Soltys2026-09-031-2/+13
| | | | | | | first 9s after refresh showed ~800ms of secp256k1 (q0/mul/TJ) dominating the main thread; the source was _key(index) recomputing a BIP32 derive + a getPublicKey point-mul on every call, and reconcile() calls it for every spendable coin (56 here), with decode/send/exit/fee-math hitting it again. A key for an index is constant per wallet, so it's cached by index now (mailbox key too) — dozens of EC muls per boot collapse to one each Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The post-refresh drag stops lurching: ark's state persistence was the stall. ↵Adam Soltys2026-09-031-2/+42
| | | | | | | Every _save() — fired dozens of times during a boot reconcile — did a JSON.parse of stored state, a merge over every vtxo and movement, a JSON.stringify, a synchronous localStorage write, and saveCache(). A CPU profile of the first 9s pinned it: ~230ms in saveFeatureState/loadFeatureState/setItem bursts blocking the main thread, which delayed the mouse drag's scrollLeft writes (mouse drag is main-thread; touch scrolls on the compositor). The in-memory state is always current and every live read uses it, so the disk write is now coalesced — immediate when idle, one write ~200ms after a burst settles (800ms cap), flushed on page-hide and wallet switch so nothing is lost Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The real 'false start': the auto-select-Spending one-shot yanking the card ↵Adam Soltys2026-09-032-1/+12
| | | | | | | back. On open, when Savings is empty and Ark has a balance, the wallet flips you to Spending — a convenience. But it fires on every Ark update and only latches once the balance SYNCS, which after a refresh arrives a second late — right after you've dragged to Savings, so it overrides your choice and slides back. Now any deliberate pick (carousel scroll/tap/flick, swipe, accounts screen) sets accountUserChosen, and the auto-select consumes its one-shot without flipping when you've already chosen Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Two carousel polish fixes. The 'slight move after history loads' was the ↵Adam Soltys2026-09-032-1/+12
| | | | | | | vertical scrollbar: a short page has none, the history list filling in makes the page tall enough to get one, the viewport narrows ~15px, and the snapped card re-centers into the narrower width — a horizontal nudge right as history lands. scrollbar-gutter:stable reserves the gutter so growing content never changes the width. And the 'false start' — a boot render's alignment firing a scrollTo mid-drag — is guarded: alignment now bails while a finger/mouse is down Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The carousel stops snapping back mid-throw: on release the mandatory ↵Adam Soltys2026-09-031-1/+16
| | | | | | | snap-type was restored on a fixed 400ms timer, which during rapid back-and-forth fired while the next glide was only partway — re-applying mandatory snap then makes the browser snap to the nearest point, the card you started from, yanking it back. Snap now restores on the scrollend of that specific glide, guarded by a per-drag token so a stale restore from an earlier throw can't fire during a later one (timer kept only as a fallback for the no-scroll case) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The balance carousel stops stuttering during boot: the post-render alignment ↵Adam Soltys2026-09-032-5/+24
| | | | | | | no longer reads layout (offsetLeft/scrollLeft) on every one of the many boot renders — an _alignedTo guard short-circuits before any layout touch once the card is in place, and the scroll's own settle marks itself aligned so it never re-aligns behind the user. The pass moved to rAF, and each slide gets layout containment so the history list building in below can't force it to reflow mid-scroll Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* History stays home under the panes: Receive and Send now fold open ABOVE the ↵Adam Soltys2026-09-031-6/+16
| | | | | | | transaction history instead of replacing it, so returning to the list is a scroll, not an unintuitive re-tap of the toggle. A tapped history detail (or the receive celebration) still claims the whole area Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Copy preimage joins the cluster too: no longer a lone full-width block, now ↵Adam Soltys2026-09-031-2/+3
| | | | | | | a small button in the grouped row with copy tx id / view explorer / copy address — one consistent size for every secondary copy action Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The send/gift detail's small buttons cluster instead of scattering: every ↵Adam Soltys2026-09-031-11/+20
| | | | | | | secondary copy/view action (copy gift link, copy tx id, view on explorer, copy address) now rides one wrapping row at its natural width, rather than each stretching full-width and getting split apart by the block buttons between them. Revoke (now danger-styled) and Back keep the full width they deserve as primary actions Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The rail glyph tells the truth on-chain: board, offboard (Moved to Savings) ↵Adam Soltys2026-09-031-6/+19
| | | | | | | and exit each settle as a real bitcoin tx — txid, bc1q address, block explorer — so they wear the Bitcoin mark in the history row and detail header instead of the Ark logo; pure off-chain hops (receive/send/renew) keep the Ark mark, Lightning keeps its bolt Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Reconcile rows earn a real detail page: date, amount, and every consumed ↵Adam Soltys2026-09-033-9/+48
| | | | | | | coin id — the folded row rides ui state (and nav history) since it isn't a stored movement. Ordinary send details also stop hiding their inputs: 'Coins spent' lists what a payment gathered (up to 24), beside the destination vtxo it minted Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The reconcile row sheds its costume entirely: just Sent, a time, an amount ↵Adam Soltys2026-09-032-19/+10
| | | | | | | — an ordinary send row, because it was the user's own spend and nobody needs an alarm about which device made it. (The ASP's status API returns only a spent bit, so the shown time is when this wallet caught up) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* 'Spent on another device' stops sounding like an accusation: the reconcile ↵Adam Soltys2026-09-032-6/+18
| | | | | | | row is now a plain Sent wearing a quiet 'synced' tag, and tapping it unfolds the reassurance — the coins left your wallet in a session this device didn't see (another device, a restore that dropped history, or a server-side renewal), and the balance is correct; the row just keeps the history adding up Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A cold boot can no longer dress a real account in a punk for a day: an empty ↵Adam Soltys2026-09-031-2/+17
| | | | | | | profile answer (the 5s query racing still-dialing relays) is retried in minutes instead of being trusted for the 24h TTL, faceless rows are never persisted (they were evicting real faces from the 150-cap), and the logged-in identity's own profile is pinned against eviction entirely Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* An offboard's price stops hiding in one number: the miners' cut and the ↵Adam Soltys2026-09-025-10/+45
| | | | | | | ASP's service charge (base + lifetime ppm) record separately and the history detail shows them on their own lines — settling the mystery of a ~350-sat chain fee inside a ~650-sat total. The community row's member count anchors to last session's settled figure instead of ticking upward through the guestbook replay, and a profile's Pay button reached from search actually reaches Send — the lingering search screen was winning the router Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The Amethyst handshake survives its own launch: opening the signer app ↵Adam Soltys2026-09-022-13/+123
| | | | | | | freezes the tab (relay sockets die as the approval publishes) and its callbackUrl then RELOADS the page, discarding the client key mid-handshake — so the pending connect now persists to sessionStorage, the wait races the live subscription against a stored-event poller kicked on every return to visibility, and a fresh boot resumes the handshake instead of shrugging. Proven both ways with a scripted signer: approval after reload, and approval stored while the page was gone Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The black flash on Get started dies at the morph: a reused button whose ↵Adam Soltys2026-09-021-0/+10
| | | | | | | class changes between screens is a different control wearing the same node, and its background transition was crossfading the old style into the new one — repaints aren't style animations, so the swap now snaps and the transition comes back after the paint Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The drag holds its tongue until you let go — no account switch while the ↵Adam Soltys2026-09-024-30/+65
| | | | | | | button or finger is still down (release is the choice, shared settle logic for mouse, touch and glide) — and the coins table's dotted meta line becomes little labeled pills: an hourglass countdown, depth, and exit cost each in their own chip, the expired one wearing the warning color Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The single-file wallet returns as a first-class artifact: every build now ↵Adam Soltys2026-09-022-3/+18
| | | | | | | emits standalone.html (~950KB, everything inlined, boots from a bare file:// with no server), How it works grows a 'Keep a copy' card linking the download, and the tab strip's phantom double-tap dies — the pointerdown's paired click was arriving after the render had already swapped in fresh handlers, and with toggling tabs that ghost click un-did the tap it belonged to. The timestamp guard now lives on the DOM node, which survives the morph Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The mouse drag learns to catch a flick: release velocity over the last ↵Adam Soltys2026-09-021-7/+28
| | | | | | | ~100ms throws the card in the flick's direction, so a quick short pull changes accounts instead of demanding a half-width haul; a slow release still settles nearest Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* History becomes home: the tab bar slims to Receive | Send (tapping the lit ↵Adam Soltys2026-09-025-31/+109
| | | | | | | one puts it away), every landing — login, account creation, an account snapped into the carousel — is the account's own timeline, and that timeline is now SCOPED: Savings shows the on-chain ledger and gift reservations, Spending shows the ark side. The carousel meanwhile learns mouse and mind-reading: desktop gets a real grab-and-pull drag (snap lifted mid-drag, restored on the glide), the peeking neighbor switches on a single click, and the strip wears grab cursors. And a thread's avatars and author names finally go somewhere: profiles stack on top of the conversation and back returns to it Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The carousel becomes what it was meant to be: two separate balance CARDS ↵Adam Soltys2026-09-022-57/+65
| | | | | | | riding a full-bleed strip — the neighbor card peeks in from the screen edge and snaps into place, instead of two faces crowding one card's chrome. The active card carries the move/top-up buttons and unfold panel; the peeking one stays a clean preview Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The balance grows a card carousel: Spending and Savings become side-by-side ↵Adam Soltys2026-09-022-37/+92
| | | | | | | faces with the neighbor peeking at the card's edge, dragged and snapped by native scroll-snap — settling on a face IS the account switch, programmatic switches glide the carousel to match, and the old swipe-to-flip gesture yields to the carousel's own drag (it still serves single-face wallets) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The scanner's close glyph finds the center of its circle: the base button ↵Adam Soltys2026-09-021-1/+8
| | | | | | | padding (11px/16px, asymmetric in a fixed 42px round) was shoving the ✕ off-center — flex-centered with the padding dropped Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* nevent and nprofile references come alive: nprofile mentions render as ↵Adam Soltys2026-09-023-5/+39
| | | | | | | clickable @names beside their npub siblings, and note/nevent references become 'view note' links that fetch the event by id (relay hints first) and open its thread right in the app — the dim truncated stubs survive only for what genuinely can't resolve Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A raw npub in a bio stops punching through the profile card: the about text ↵Adam Soltys2026-09-021-1/+8
| | | | | | | renders through noteBody like any note — npub mentions become clickable @names, other nostr: refs truncate to stubs, URLs linkify — with overflow-wrap catching whatever unbreakable string remains Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Recipient search warms its pipes on focus, not first keystroke: the Primal ↵Adam Soltys2026-09-023-23/+47
| | | | | | | cache socket, the registrar's TLS session, and the NIP-50 relay connections each cost a handshake that used to land on the first query — the cold second of dead air. Touching the send recipient field, opening user search, or unfolding New message now pre-opens all three (throttled; the idle reaper still closes the socket a minute after real use) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Two small trims: the payment detail's copy-preimage becomes a full-width ↵Adam Soltys2026-09-022-7/+3
| | | | | | | button matching the Back beneath it, and the receive page's lightning address drops its edit pencil — renaming lives in the profile editor's username field, and two doors was one too many Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The 200ms of secp256k1 hiding in every repaint: the depth advisory priced a ↵Adam Soltys2026-09-022-2/+21
| | | | | | | full uncooperative exit — every hop of every coin, an EC tweak per hop — on each wallet render; the estimate now memoizes on coin set + feerate, and decoded vtxos cache by id (bytes are immutable). Profiled on a real phone: point add/double/wNAF was 1.3s of a 4.3s six-tab-switch trace Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Three render hogs put on a leash for the sluggish-tabs hunt: qrSvg is ↵Adam Soltys2026-09-023-3/+44
| | | | | | | memoized (make() runs eight mask trials over a version-20 grid — 30ms+ per call on a phone, paid on EVERY render of the receive pane), the header's unread count reuses its answer behind a revision counter bumped only when messages or read-marks actually change, and ark's history list rebuilds at most every 1.5s instead of per repaint Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Tab feedback at touch speed: the strip's active state flips with a direct ↵Adam Soltys2026-09-021-2/+14
| | | | | | | DOM write that paints in the same frame as the finger, and the heavy pane render runs after that paint (double-rAF) — running it synchronously on pointerdown meant even the button's own light-up waited out a 100ms+ rebuild Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Tabs answer the finger, not the click: switching happens on pointerdown (the ↵Adam Soltys2026-09-022-3/+23
| | | | | | | click event lands 100-250ms after the touch, and a tab has no scroll ambiguity to wait out — keyboard activation keeps the click path), every button takes touch-action:manipulation so double-tap-zoom arbitration stops taxing quick taps, and the sliding pane pre-promotes to its own compositor layer — a slow mid-slide re-render was letting Chrome expose unpainted gray tiles at the viewport edges Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The paying spinner narrates its retries: a Lightning pay that fails its ↵Adam Soltys2026-09-022-1/+6
| | | | | | | route and re-attempts with a bumped fee budget now says so ('Route failed — retrying with a bigger fee budget (2/3)') instead of three-plus minutes of silent 'Paying invoice…' — a tester paying an unreachable node watched that spinner stonewall through every auto-retry Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The savings wallet gets a mirror it can always reach: ↵Adam Soltys2026-09-021-0/+5
| | | | | | | wss://halwallet.app/electrum joins the mainnet sources right behind the default — the coinos.io zone edge 403s WS clients with poor IP reputation and the public fallbacks are the usual regional-block suspects, so failover now lands on our own Fulcrum through a different apex first Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Max reaches every Lightning amount form: amountless bolt11s, ark zaps, and ↵Adam Soltys2026-09-022-0/+32
| | | | | | | lightning-address zaps all fill spendable-minus-estimated-fees (sum over the 24-input cap, schedule fee plus the routing reserve, real quote confirms at pay time) — emptying Spending over Lightning stops being arithmetic homework Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The tester with 233 sats gets to spend them: invoice coverage judges the SUM ↵Adam Soltys2026-09-023-7/+32
| | | | | | | of spendable coins (the single-coin gate silently declined anything over the largest coin, though multi-input pays gather 24), an uncoverable invoice now owns its paste — board the shortfall from Savings, or say what's short — instead of dropping the bolt11 into the address field like a dead click, and chain calls gain a last-resort relay through the registrar (/esplora passthrough, tip cached) for the network that blackholes every public esplora Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Cold boots stop lying for a few seconds: an empty profile answer (relays ↵Adam Soltys2026-08-312-2/+28
| | | | | | | still dialing) no longer clobbers a remembered avatar with a punk — stale fields hold while the refresh does its rounds — and the ark merge marks coins spent the moment a synced movement or done action names them as consumed inputs, instead of double-counting a sleeping device's view (output merged in, inputs still 'spendable') until the reconcile caught up and the balance visibly ticked Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The chain adapter learns a second road: mainnet esplora calls fail over ↵Adam Soltys2026-08-312-8/+42
| | | | | | | between the configured host, blockstream.info, and mempool.space (remembering whichever answered last, rotating on network failures, 429s and 5xx) — one tester's network simply cannot reach mempool.space and every chain-touching feature was dying for it, stranded mailbox receives included. And a failed quote no longer wedges the Lightning pay form at an eternal spinner: it prices at the outage fallback and lets Pay proceed, where the payment path quotes again for real Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Bills meet their bands: the hard hat's and trucker cap's side bills get flat ↵Adam Soltys2026-08-311-2/+2
| | | | | | | undersides that land exactly on the band's bottom edge instead of a curved lip that dipped past it and left a visible seam at the junction Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The hard hat goes safety-orange and gets a trucker-style bill off the right ↵Adam Soltys2026-08-311-4/+5
| | | | | | | side, band and bill in the darker shade Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A hard hat for the crew that builds this with us: an operator-giftable ↵Adam Soltys2026-08-312-3/+14
| | | | | | | construction hat joins the catalog (never listed for sale — like the crown it appears only in a rack that holds it), the registrar grows POST /hats/grant behind the admin token (hex or npub, crown excluded), and a gifted hat settles into the rack instead of knocking a paid hat off a bare-headed recipient wears it at once Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The mailbox cursor stops leaping wounded messages: it advances only after a ↵Adam Soltys2026-08-311-2/+31
| | | | | | | message's work is DONE (a final rejection counts, a transient failure does not), holds at the lowest failed checkpoint so a healthy message behind a sick one can't skip it, and the poll defers the rest of a batch on failure — one esplora hiccup mid-validateVtxo used to skip an arkoor receive forever (monogram's 204 sat, twice) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The wizard hat stops demanding 42k sats of phantom headroom: the shop's ↵Adam Soltys2026-08-311-2/+5
| | | | | | | affordability check drops from 2% to max(10, 0.2%) routing allowance — hat invoices live on the ASP's own node where the quoted route fee is a couple of sats, and a wallet holding 2,133,786 was told it couldn't afford a 2,100,000 hat Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr