Skip to content

Extract wallet logic into @agicash/wallet-sdk (live stores)#1157

Draft
ditto-agent wants to merge 159 commits into
masterfrom
sdkx/store
Draft

Extract wallet logic into @agicash/wallet-sdk (live stores)#1157
ditto-agent wants to merge 159 commits into
masterfrom
sdkx/store

Conversation

@ditto-agent

Copy link
Copy Markdown
Contributor

Extracts all wallet business logic + background processing into @agicash/wallet-sdk so it can run headless (MCP / node), not just inside the React app: Cashu + Spark send/receive, accounts, transactions, auth, the six payment state-machine processors, and leader election.

Store-based approach: the SDK keeps hot reads fresh as live Store<T> views (get / subscribe / toPromise), so no frontend re-implements a cache. The web app reads them via useStore / useStoreSuspense / useStoreSelect and drops its in-app cache + realtime layer entirely; mutations route through sdk.*.

Engine: a hidden, patched @tanstack/query-core (a MutationObserver-scope task runner + the resident stores), fully confined to internal/engine/ behind an engine-neutral Store<T> (enforced by lint + a test). Also ships the shared lib extraction (@agicash/money · bolt11 · ecies · cashu · utils) and a server-mode SDK for the Lightning-address routes.

Status: headless gate green (typecheck + unit tests); browser-verified through guest sign-in + wallet load (no empty-store flash). Live money-path verification (Lightning send/receive, /lnurl-test) still owed → draft.

🤖 Generated with Claude Code

ditto-agent and others added 30 commits June 11, 2026 17:45
…ore-based)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- sharpen goal: SDK owns business logic + background in both; caching is the
  contested axis (SDK in B, frontend in A)
- rename payment:* lifecycle events -> send:*/receive:*; lock all-instance delivery
- split event map: shared SdkCoreEventMap + A-only row events (explicit
  created/updated, not upserted); B exposes core only
- resolve transactions: Promise-based in both (no store); current wallet does not
  live-update history (verified) and SWR-on-visit is preserved in both variants
- define resync() as coarse, idempotent catch-up trigger

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Headless @supabase/realtime-js 2.95 under bun: socket open, broadcast
round-trip, postgres_changes subscribe — no window, TLS via mkcert CA.
No polling fallback needed; SDK-owned background design stands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tern + recipe)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
entry.client.tsx used a relative path `./lib/money` that was not caught
by the ~/lib/money → @agicash/money rewrite pass, causing an
UNRESOLVED_IMPORT build failure (170 modules → error vs 2821 after fix).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Declare workspace dep, codemod relative imports (not just ~/ alias), and
run build as a gate (tsc misses dangling relative imports under Bundler
resolution). Note the DOM/window devtools-formatter follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ind regex)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h/money

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…biome-only)

fix:all is biome check (lint/format) and never type-checks; tsc is the
catch-all for dangling imports (all forms); the build erases import type
so it misses type-only dangling imports. This is why money shipped 3
dangling ../money imports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves apps/web-wallet/app/lib/bolt11/ into packages/bolt11/src/, rewires
all 10 import sites from ~/lib/bolt11 to @agicash/bolt11, removes
light-bolt11-decoder from the app's direct deps (now owned by the package),
and adds @agicash/bolt11 as a workspace dep to apps/web-wallet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves ecies/, sha256.ts, and xchacha20poly1305.ts from apps/web-wallet/app/lib into packages/ecies, hoists @noble/ciphers to the workspace catalog, and rewrites all 8 import sites to @agicash/ecies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…package

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exempt first-party @agicash/opensecret from bun minimumReleaseAge so the
intentionally-fresh 1.0.0-rc.0 (storage provider) installs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…secret rc.0

rc.0 makes `storage` required on OpenSecretConfig (pluggable StorageProvider).
Browser host passes the bundled browserStorage helper. Unblocks the app
typecheck after the catalog bump in the previous commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ovider bridge

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s internal

database.ts + cashu/spark account-details json-models move into the SDK
(app re-exports them via shims). Generated supabase/database.types stays at
repo root, imported relatively. Lightning/swap/melt models stay in the app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…able)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…th:session-expired)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ditto-agent and others added 26 commits June 21, 2026 00:47
…r-lane, re-entrant)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ueryFns)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…online filter)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ts store)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Store hot reads

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(no placeholder store seed)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add disposeAll() to all 3 NUT-17 subscription managers (mint-quote,
melt-quote, proof-state); forward fire-and-forget from each tracker's
dispose() so registry.deactivate() → processor.dispose() →
tracker.dispose() → manager.disposeAll() cleanly unsubscribes per-
subscription WebSocket callbacks without closing the shared per-mint
socket. ProofStateTracker's explicit no-op is replaced with the teardown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-add two base-domain methods that Variant B's web cut-over needs but
were absent from the frozen base (a210e9d). Exact reproduction of
Variant A (sdkx/stateless) additions: UserDomain.setDefaultCurrency
(Currency import + writeUserRepo.update patch) and
CashuSendOps.getSwap (swapRepository.get pass-through with JSDoc).
Full TDD: red→green, 197/197 pass, typecheck exit-0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates ~/lib/storage-adapter.ts (verbatim copy of Variant A's lazy
localStorage/sessionStorage adapters) and ~/lib/sdk.ts (Variant A copy
with 3 swaps: StatelessSdk→StoreSdk, createStatelessSdk→createStoreSdk,
import from @agicash/wallet-sdk/store). Adds fire-and-forget
initSdk(location.host) kickoff in _protected.tsx after the auth gate
(strangler pattern; B-Task-4/5 will await). Gate: typecheck exit 0,
254 tests across all packages, 0 fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e/useStoreSelect)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- auth.ts: getAuthSdk = initSdk(location.host); rewire all useAuthActions
  methods onto sdk.auth.* (signUp/signIn/signOut[+disposeSdk]/beginGoogle/
  signInGuest/requestPasswordReset/confirmPasswordReset[positional→object]/
  verifyEmail/upgradeGuest); delete useHandleSessionExpiry + private helpers;
  keep authQueryOptions/isLoggedIn/sessionHintCookie/Sentry-user verbatim
- _auth.oauth.$provider.tsx: handleGoogleCallback → sdk.auth.completeOAuth
- verify-email.ts: osVerifyEmail → getSdk().auth.verifyEmail
- user-hooks.tsx: requestNewVerificationCode → getSdk().auth.requestEmailVerification;
  convertGuestToFullAccount drops guestAccountStorage.clear() (SDK manages guest state);
  useUser/userQueryOptions LEFT UNTOUCHED (B-divergence: BW-T7 converts to store)
- wallet.tsx: remove useHandleSessionExpiry call (host effect moves to BW-T5)
- _protected.tsx: delete ensureUserData + key-derivation block; await initSdk +
  sdk.user.get() + sdk.user.acceptTerms() for pending terms; setQueryData user

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… bridge (delete leader)

<Wallet> becomes the SDK background boundary: background.start()/stop() on
mount/unmount, useSDKActivityTracking forwards online/offline/visibility, focus
+ online trigger sdk.resync(), and auth:session-expired toasts + signs out. The
app's leader-election/TaskProcessor is deleted (the SDK owns processing — single
leader). useTransactionLifecycleSync (B-NEW) invalidates the kept transaction
queries off core lifecycle events since B has no transaction row events.

KEPT: useTrackWalletChanges (shrinks over BW-T6..T11, deleted in BW-T13),
Sentry.setUser, useSyncThemeWithDefaultCurrency, useTrackAndUpdateSparkAccountBalances.
disposeSdk() stays on the auth.ts sign-out boundary (matching Variant A).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Convert the accounts read surface from the TanStack AccountsCache /
accountsQueryOptions to the SDK sdk.accounts.all (Store<Account[]>) +
sdk.user.current stores via the BW-T3 useStoreSuspense adapter.

- useAccounts now reads both stores, overlays live spark balances, and
  runs getExtendedAccounts + filter/sort in a useMemo (returns the array
  directly; consumers updated to drop the { data } destructure).
- All single-account derivations (useAccount, useAccountOrDefault,
  useDefaultAccount w/ useRef fallback, useBalance, useGet* getters,
  useGetCashuAccountByMintUrlAndCurrency) derive off the store snapshot.
- useAccountOrNull sources from the store; lazy-fetches expired accounts
  from the residual accountRepository on store miss.
- useAddCashuAccount.mutationFn -> sdk.accounts.add (fanout writes store);
  onSuccess cache upsert removed.
- Spark live-balance overlay: new live-spark-balances useSyncExternalStore
  module map; useTrackAndUpdateSparkAccountBalances writes to it.
- Delete AccountsCache + useAccountsCache + useAccountChangeHandlers +
  accountsQueryOptions; shrink use-track-wallet-changes (account handler +
  invalidate removed, file kept). Trim account-service to statics.
- Repoint gather blind-spots off the deleted symbols: claim-cashu-token
  -service (+ its route wiring), cashu-send-quote usePendingMeltQuotes,
  send-provider, _protected.send loader.

Gate: bun run typecheck (exit 0) + bun run test (0 fail). Sweep empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Convert useUser + module readers from the TanStack UserCache/userQueryOptions
to the sdk.user.current Store<User|null> via the BW-T3 store hooks, and repoint
the 4 user mutations to sdk.user.*.

- useUser(select?) -> useStoreSelect(getSdk().user.current, ...) with a
  throw-on-null guard; call signature + direct return shape preserved so the
  ~21 selector-form consumers are untouched.
- getUserFromCache()/getUserFromCacheOrThrow() -> sdk.user.current.get().
- useSetDefaultCurrency/useSetDefaultAccount/useUpdateUsername/useAcceptTerms ->
  sdk.user.{setDefaultCurrency,setDefaultAccount,updateUsername,acceptTerms};
  drop the onSuccess setQueryData pokes (the SDK fanout writes the store).
- Delete UserCache + useUserCache + useUserChangeHandlers + userQueryOptions +
  internal useUpdateUser; shrink use-track-wallet-changes (drop user
  handler/cache, keep the file).
- _protected: repoint the user seed to sdk.user.current.set(() => user) so the
  synchronous accept-terms / verify-email route guards (which read in the
  middleware chain before render) keep working; drop the UserCache import.
- claim-cashu-token-service: remove the dangling UserCache setQueryData poke
  (BW-T6 carry-forward); the SDK setDefaultAccount/fanout owns the user store.

Gate: bun run typecheck (all packages exit 0) + bun run test (web-wallet 57/0,
wallet-sdk 197/0). Sweep for UserCache/useUserCache/userQueryOptions/
useUserChangeHandlers in apps/web-wallet/app is empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- useContacts/useContact read from sdk.contacts.all via useStoreSelect (suspends until loaded)
- useCreateContact/useDeleteContact delegate to sdk.contacts.add/remove; no cache writes
- useFindContactCandidates queryFn repointed to sdk.contacts.search
- Delete ContactsCache, useContactsCache, useContactChangeHandlers
- Delete contact-repository.ts (no remaining app consumers)
- Shrink use-track-wallet-changes: remove contactChangeHandlers + contactsCache.invalidate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nvalidation

- queryFns for useTransaction / useTransactions / useHasTransactionsPendingAck /
  useAcknowledgeTransaction all point to getSdk().transactions.*
- TransactionsCache reduced to a key-only static holder (exported); instance
  methods + useTransactionsCache + useTransactionChangeHandlers deleted
- use-track-wallet-changes: removes transaction handlers + transactionsCache.invalidate
- use-transaction-lifecycle-sync: reconciled from inline literals to TransactionsCache.Key
- cashu/spark-receive-quote-hooks: adapted residual invalidateTransaction call to
  queryClient.invalidateQueries on TransactionsCache.Key directly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Repoint send mutations to sdk.{cashu,spark}.send.* (create-only, copied
from Variant A): createLightningQuote/execute for cashu+spark lightning;
createTokenSend for the cashu token send (returns {token, swap:PENDING}).

Convert the active cashu-send-swap tracker to lifecycle-event liveness:
useCashuSendSwap/useTrackCashuSendSwap read getSdk().cashu.send.getSwap(id)
and refetch on sdk.on('send:completed'|'send:failed') filtered by the swap
id (carried as payload.quoteId). B has no row events; terminal swaps are
evicted from the SDK store. The share route renders the token immediately.

Delete the 4 send caches (UnresolvedCashuSendQuotesCache, CashuSendSwapCache,
UnresolvedCashuSendSwapsCache, UnresolvedSparkSendQuotesCache) + their change
handlers + the dead useProcess*Tasks/useOn*StateChange/usePendingMeltQuotes/
useUnresolved* selectors + proof-state-subscription-manager.ts. Remove the
send change-handlers from use-track-wallet-changes.ts. The 6 send service/repo
files stay (pinned by transfer/transaction-additional-details).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eive

Repoint receive mutations to create-only sdk.{cashu,spark}.receive.{createLightningQuote,execute}
(copy Variant A's contract; drop app cache adds). Convert the active per-id quote trackers to
app useQuery(sdk.*.receive.get) + lifecycle-event liveness (receive:completed / receive:expired
filtered by protocol+quoteId → refetch), since B has no row events. Wire the deep-link ?claimTo
claim onto sdk.cashu.receive.receiveToken (try/catch, DomainError toast, gift-card redirect) and
the interactive claim onto the 6d surface (createTokenClaim / getClaimableToken / getTokenAccounts);
keep the signed-out _public placeholders and re-source token-receive types from @agicash/wallet-sdk.

Delete the 5 receive caches + change-handlers + dead processors + the 3 lib/cashu mint/melt
subscription managers + the orphaned claim service files (claim-cashu-token-service,
receive-cashu-token-quote-service, cashu-token-melt-data) and the now-orphaned
cashu-receive-swap-hooks. Empty the receive change-handlers out of use-track-wallet-changes
(now a no-op; BW-T13 deletes it).

Gate: bun run typecheck (all workspaces exit 0) + bun run test (web-wallet 57 pass/0 fail,
wallet-sdk 197 pass/0 fail). Browser/live verification owed to BW-T15.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Repoint useGetTransferQuote and useInitiateTransfer mutations to
getSdk().transfers.createQuote / getSdk().transfers.execute. Drop
useUser (userId now SDK-resolved) and useTransferService. Re-source
TransferQuote from @agicash/wallet-sdk.

transfer-service.ts kept: still pinned by transfer-confirmation.tsx
and transfer-store.ts (TransferQuote type + useTransferService).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete use-track-wallet-changes.ts (was already a no-op after BW-T6..T11)
  and remove its call + import from wallet.tsx.
- Delete lib/supabase/{supabase-realtime-hooks,supabase-realtime-manager,
  supabase-realtime-channel,supabase-realtime-channel-builder,index}.ts —
  the SDK's ChangeFeed owns realtime; the app-side transport is gone.
- Strip agicashRealtimeClient construction + window.agicashRealtime from
  database.client.ts; agicashDbClient (DB client) stays for BW-T14 residuals.
- Inline SupabaseRealtimeError class into root.tsx to preserve the error
  boundary branch (BW-T14 removes it once the realtime error path is gone).
- Final sweep confirms zero remaining references to the deleted symbols.

Gate: typecheck exit-0 (all 8 packages), test 254 pass / 0 fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Supabase Residual (carried — identical to Variant A / AW-T14)

`agicashDbClient` and `supabase-session.ts` are intentionally kept.
Consumers verified via grep:

- `features/agicash-db/database.client.ts` — defines `agicashDbClient` itself
- `features/agicash-db/supabase-session.ts` — imported by `database.client.ts`
  for `getSupabaseSessionToken`
- `features/shared/feature-flags.ts` — `agicashDbClient.rpc('evaluate_feature_flags')`
  (feature flags RPC; no SDK equivalent)
- `features/accounts/account-repository.ts` — pinned by `account-hooks.ts`
  (`useAccountOrNull` lazy expired-account DB fetch via `useAccountRepository()`)
- `features/receive/cashu-receive-quote-repository.ts` — pinned by
  `transaction-additional-details.tsx` (4× `getByTransactionId` calls for
  cashu-receive-quote + cashu-receive-swap details)
- `features/receive/cashu-receive-swap-repository.ts` — same
- `features/send/cashu-send-quote-repository.ts` — pinned by
  `transaction-additional-details.tsx`
- `features/send/cashu-send-swap-repository.ts` — pinned by
  `transaction-additional-details.tsx` + `transaction-hooks.ts`
  (`useReverseTransaction` via `cashuSendSwapRepository.getByTransactionId`)
- `features/user/user-repository.ts` — WAS residual; deleted (see below)

`opensecret.configure()`, Breez WASM prefetch, and Sentry init in
`entry.client.tsx` are all kept (auth canary + live dependencies).
`_protected.tsx` has no `supabaseSessionTokenQuery` prefetch (SDK self-warms).

## root.tsx: SupabaseRealtimeError removed

Removed the inlined `SupabaseRealtimeError` class (BW-T13 interim) and its
`instanceof SupabaseRealtimeError` ErrorBoundary branch. The SDK never throws
a realtime error to the boundary — connection failures degrade via the
`connection:state` event. The remaining branches (NotFoundError, DOMException/
SecurityError, WebAssemblyUnavailableError, generic Error) are intact.

## Dead-file sweep (11 deleted + 2 type-imports re-sourced)

Deleted (all grep-verified app-wide before deletion):
- `features/user/guest-account-storage.ts` — BW-T4 dropped its last use
- `features/transactions/transaction-repository.ts` — app-orphaned; the
  residual is the CASHU repos via `getByTransactionId`, not this tx repo
- `features/transfer/transfer-service.ts` — `TransferQuote` type re-sourced
  to `@agicash/wallet-sdk` in `transfer-confirmation.tsx` + `transfer-store.ts`
- `features/user/user-service.ts` — orphaned; all user writes now via `getSdk().user.*`
- `features/user/user-repository.ts` — orphaned; only consumer was `user-service.ts`
- `features/receive/cashu-receive-quote-service.ts` — orphaned when
  `transfer-service.ts` deleted (was its only caller)
- `features/receive/spark-receive-quote-service.ts` — same
- `features/receive/spark-receive-quote-repository.ts` — only consumer was
  `spark-receive-quote-service.ts`
- `features/receive/spark-receive-quote-core.ts` — only consumer was
  `spark-receive-quote-service.ts` / `spark-receive-quote-repository.ts`
- `features/send/cashu-send-quote-service.ts` — orphaned when
  `transfer-service.ts` deleted; `CashuLightningQuote` type re-sourced
  to `@agicash/wallet-sdk` in `send-store.ts`
- `features/send/spark-send-quote-service.ts` — same; `SparkLightningQuote`
  type re-sourced to `@agicash/wallet-sdk` in `send-store.ts`
- `features/send/spark-send-quote-repository.ts` — only consumer was
  `spark-send-quote-service.ts`

Kept with pins documented:
- `features/send/cashu-send-swap-service.ts` — active (transaction-hooks,
  send-confirmation, cashu-send-swap-hooks, send-store)
- `features/receive/cashu-receive-swap-service.ts` — active (5 consumers)
- `features/receive/cashu-receive-quote-core.ts` — pinned by
  `cashu-receive-quote-repository.ts` residual

Gate: typecheck exit-0, 289 tests (197 SDK + 57 web-wallet + 35 cashu) / 0 fail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Empirical eval of the two complete variants (A=sdkx/stateless, B=sdkx/store).
Result is a structural split: every B win traces to its CONTRACT (SDK owns
hot-read freshness via Store<T> → app keeps 1 key-holder + 3 hooks vs A's 10
cache classes + 30 wire hooks), every B loss to its ENGINE (patched query-core
→ readability/headless/node-bundle costs). They are separable behind the frozen
internal/engine seam → the eval is the strongest evidence for the spec's
deferred Variant C (B's contract on a hand-rolled engine). Dim 4 (behavior
parity) owed on live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Local browser verification (guest session) clears the highest-value B-specific
risks: boot+hydrate, guest sign-in end-to-end, the BW-T7 sync-route-guard fix,
NO empty-store flash (accounts store populated + default resolved — the A
ensureLoaded-bug code path, clean in B), zero console errors, app-QueryClient
reads (rates + transactions). Money-path / multi-instance / Lightning checks
remain owed (need funded wallets + a 2nd instance).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agicash Ready Ready Preview, Comment Jun 22, 2026 9:54am

Request Review

@supabase

supabase Bot commented Jun 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project hrebgkfhjpkbxpztqqke because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant