`LocalCache` (4,021 lines) had no Android imports and no app-package types —
only six references to the shell around it. It moves to
`commons/jvmAndroid/…/commons/model/cache/`, with `AntiSpamFilter`,
`CachePruner`, `CacheSearch`, `DvmHeartbeatRegistry` and `OnchainZapResolver`.
This is part A of the sweep plan's Wave 2; `desktopApp` still runs its own
`DesktopLocalCache` fork and is untouched here.
The six references become `LocalCacheHost`, a port collecting what the cache
cannot supply for itself: a scope, `isDebug`, the NIP-95 blob directory, the
relay stats sink, the relay's own pubkey and the main-thread assertion. Every
member defaults to the honest answer for a host that supplies none of them, so
the cache runs shell-less; Android installs `AmethystLocalCacheHost` from
`AppModules` next to `val cache`, before any event is consumed. The whole seam
cut is 101 changed lines. `dateFormatter` repoints to the twin that already
existed in `commons/util`.
`nip95BlobDir` is nullable, so `consume(FileStorageEvent)` grows one branch:
with nowhere to spill the blob, the note keeps the event whole rather than
loading a content-stripped copy whose bytes are on no disk. Android always
supplies the directory, so its behaviour is unchanged.
The plan flagged `CachePruner`/`CacheSearch` taking `Account` as needing a
design decision. `IAccount` needed no new members and Desktop needed no change:
`isHidden`, `hiddenWordsCase` and `followingKeySet()` are already on it, and
`isFollowing(user)` is `user.pubkeyHex in followingKeySet()`. `pruneHiddenEvents`
takes `LiveHiddenUsers` by value instead — `IAccount` exposes hidden users only
as hash codes, which would prune authors the reader never muted.
Also: `ILocalCache` was declared inline in `LocalCache.kt` and used nowhere
else, so it is hoisted to commonMain as the write-side port beside the
read-side `ICacheProvider`; `AntiSpamFilter` swaps `android.util.LruCache` for
`androidx.collection.LruCache`, whose Kotlin-nullable `get` makes the two
duplicate checks read each entry once into a local; and `njumpLink` moves to
`commons/util/ExternalLinks.kt` so the filter can log one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkULS5SVq4GHDdoCzajKi8
Custom feed creation/discovery/management for Desktop; core model + builder + kind 31890 + desktop UI shipped, but relay-filter layer, DVM marketplace, kind 10090 sync, and list resolution still pending.
Extended NIP-88 poll results page (per-option counts + who voted for what) for Android and Desktop; also specifies four tally-correctness fixes and the missing poll-relay subscription. Proposed, not started.
Split the Compose half of commons into the new :commonsUI module (same packages, api(:commons)), so cli no longer carries Compose/Skiko; records the classification method and follow-ups.