Files
minibits_wallet/__tests__
minibits-cashandClaude Opus 4.8 2f7a2763f2 Reference mints by stable id, not by url
Completes the mint-identity work. A mint url is a network locator and mints
move, but the url had become the de facto foreign key for nearly every
persisted row, so a url edit had to fan out across the schema and mostly did
not. Each table now references the mint by an identity that a move cannot
disturb, and setMintUrl shrinks to the two things that genuinely hold a url:
Mint.mintUrl and the proofs.mintUrl cache.

Which identity, per table

mintId (Mint.id) where the row needs the mint itself:

- onchain_mint_quotes. The critical one: a quote's address stays creditable for
  as long as the mint exists (rows are never deleted), so the reference has to
  outlive a move. It followed row.mintUrl, and the watcher swallows errors by
  design — a renamed mint stranded deposits permanently and silently.
- reservations. A url edit racing an open send did not merely misfile the
  proofs; commitReservation resolved the mint by url, so it threw "Mint not
  found" and aborted the commit of an operation the mint had already performed.
- transactions. `mint` was two things at once, switched by status: a historical
  record of where a finished payment happened, AND a live pointer dialled for an
  open one. That conflation is why a rename had to rewrite in-flight rows —
  rewriting the very column that records the past. mintId takes the identity
  job, so `mint` is frozen as history and the status-scoped rewrite is retired.

No reference at all where the row already has a parent:

- inflight_requests, melt_recovery are CHILD rows of a transaction (their
  primary key IS transactionId), so the parent owns "which mint". Their mintUrl
  and keysetId copies had ZERO readers — the keyset that is used comes from
  inside meltPreview. Both dropped; the one mint-scoped query joins through
  transactions.mintId. Nothing left to go stale.

Mint.id gets referential authority only, never identity authority: findById
answers "which mint is this row about?" and must never answer "are these the
same mint?" — it is random and unrelated to the keys, so that question stays
with the keysets. The backfills are IS NULL-guarded so a resolved row can never
be re-pointed at whichever mint now answers an old url.

Backfills run from JS (v38 seed), not SQL: mints live in the MST/MMKV snapshot,
so nothing in SQL can map url -> id. Matching on url is trustworthy at exactly
that moment and no other — until now a url could not change without these rows
being rewritten to match. The join is spent once, at rest, instead of on every
rename.

Migration-system fixes found along the way

- _dbVersion is now DERIVED from the migration list. It was a hand-maintained
  literal, and it was already wrong: it said 33 while migration 34 existed, so
  34 would never have run. The failure is silent and asymmetric — fresh installs
  build from schema.ts and are fine, while upgrading devices land on a schema
  the code does not have. dbMigrationRegistry.test.ts pins this and the ordering
  invariants.
- Migrations 26/28/29/31 built their tables from the LIVE schema constants. A
  device replaying them would get today's shape, and the later ALTER adding the
  column would fail with "duplicate column name" — breaking upgrades from
  exactly the versions those migrations serve. Historical shapes are frozen
  locally now, and the schema.ts header no longer recommends the sharing.
- rootStoreModelVersion was left at 37 while the seed guarded on < 38, so the
  backfill would have re-run on every launch forever.

Tests: 421 pass. Six hand-mirrored suites had drifted from the schema they
claim to mirror; transactionsMintUrl.test.ts was worse — still passing while
testing a function this commit deletes, so it is removed. The mirroring pattern
is worth revisiting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:41:00 +02:00
..
2026-06-27 23:55:40 +02:00
2026-07-08 15:44:11 +02:00