Commit Graph
16776 Commits
Author SHA1 Message Date
Claude 5976b7e466 Merge remote-tracking branch 'origin/main' into claude/concord-quartz-amethyst-plan-0oy779
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
#	amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/authCommand/model/AuthCoordinator.kt
2026-07-13 01:44:54 +00:00
Vitor PamplonaandGitHub a2e858d7b4 Merge pull request #3544 from vitorpamplona/claude/pr-note-button-sizing-732r64
Clean up Git card styling and fix duplicated subject in content
2026-07-12 21:28:11 -04:00
Claude ba0e6a0391 feat(chat): self-sufficient, reactive minichat + Concord screens; keep minichats off DMs
Every Concord/minichat screen now stands on its own regardless of how it's reached:

- MinichatScreen mounts its own datasource (the Concord plane subscription when the
  root is a Concord message; a relay reply REQ for public chats) and drives its list
  from a reactive MinichatFeedViewModel, so new replies arrive live and auto-scroll
  into view even when opened via deep link. Adds a LazyListState that scrolls to the
  newest reply as they land.
- ConcordMembersScreen and ConcordEditScreen now mount the plane subscription too
  (previously they showed nothing when opened directly), and all three community
  screens (channel list, members, edit) re-resolve sessionFor(communityId) on each
  session revision instead of caching it once — so a deep link that lands before the
  community has folded picks it up as soon as it does.

Minichats are deliberately kept OFF NIP-17 DMs: the "N replies" chip is gated to the
chat types where minichats are wired (Concord, NIP-28, NIP-29), since most clients
don't render kind-1111 replies in a DM view. The DM composer already has no thread
toggle, so DMs neither create nor surface minichats.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-13 01:21:44 +00:00
Vitor PamplonaandGitHub 82fa652c19 Merge pull request #3543 from vitorpamplona/fix/inbox-nostr-wine-over-auth
fix(relay-auth): stop paid relays billing accounts that don't use them (inbox.nostr.wine over-AUTH)
2026-07-12 21:05:35 -04:00
Vitor PamplonaandClaude Opus 4.8 6a528a742c fix(relay-auth): show a relay's payment NOTIFY only under the billed account
The relay NOTIFY (payment prompt) went into a process-wide NotifyRequestsCache
and DisplayNotifyMessages showed it under any account whose relay list contained
the relay — so a prompt billed to Amethyst surfaced under Vitor, and stale
entries re-appeared on every account switch.

A NOTIFY doesn't reliably name a pubkey, so instead of parsing the message we
correlate it with the AUTH that triggered it: a paid relay answers an
unauthorized AUTH with `OK <authEventId> false …` right before the NOTIFY, and we
signed that auth event. NotifyCoordinator remembers each auth event's signer,
maps the failing OK back to it, and files the NOTIFY into THAT account's own
Account.relayNotifications. Unattributable NOTIFYs are dropped. DisplayNotifyMessages
now reads only the current account's cache.

Also removes the now-unused app-wide RelayAuthPermissionStore singleton and wires
NotifyCoordinator with the pubkey -> Account lookup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:32:24 -04:00
Vitor PamplonaandClaude Opus 4.8 0ec63958b8 fix(relay-auth): only AUTH a relay with accounts that actually use it
One shared NostrClient serves every logged-in account, so a relay's NIP-42 AUTH
challenge is not tied to any one of them. AuthCoordinator used to fold every
account's verdict into one decision and then sign with EVERY account (or a random
throwaway key), so a paid relay like inbox.nostr.wine billed and de-anonymized
accounts that never used it — including via a merged read filter that merely named
them, and via account switches / the background notification consumer.

Decide and sign PER ACCOUNT now: an account signs only if the relay is in its own
relay list, or it is publishing its own event there (RelayAuthFirstParty), AND its
own ledger verdict (Account.relayAuthLedger) allows it. A subscription merely
naming the account is NOT first-party — that is the merged-filter false positive.
The random-ephemeral-key fallback is removed. Own-inbox reads still qualify: the
relay serving them is by definition in the account's own list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:32:24 -04:00
Vitor PamplonaandClaude Opus 4.8 f8416f4940 feat(relay-auth): per-account NIP-42 override store + state on Account
Per-relay ALLOW/DENY AUTH overrides (and the policy ledger that reads them)
lived in one process-wide DataStore keyed only by relay URL, so a DENY set for
one account silently applied to every logged-in account. Move them to a
per-account file under accounts/<pubkey>/ and warm-cache the overrides in memory
on the Account (RelayAuthPermissionCache) so an AUTH challenge is answered
without a disk read.

DataStoreRelayAuthPermissionStore now shares one DataStore per file path —
DataStore v1 forbids two live instances on one file, and loadAccount can build
the store more than once per account.

Introduces the per-account state holders wired up by the following commits:
Account.relayAuthPermissions, relayAuthLedger and relayNotifications.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:32:24 -04:00
David KasparandGitHub c2714a2f9b Merge pull request #3542 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-12 23:18:17 +01:00
Claude fe780a4220 fix(concord,chat): address audit findings (mentions, re-index perf, auth coupling, cleanups)
Follows the code-review of the dual-reply + Concord work:

1. Public-chat/relay-group minichat replies no longer drop @-mentions, hashtags,
   URL references, quotes, custom emojis, attachments, content-warning or
   expiration: ChannelNewMessageViewModel.createTemplate's MINICHAT branch now runs
   after NewMessageTagger and builds the kind-1111 from tagger.message with the same
   enrichment the inline path uses (parent author is already tagged by replyBuilder).
2. refreshConcordChannelIndex no longer runs a full re-index + ban rescan on every
   ingested message: the revision collector is sample(500)-throttled, coalescing
   bursts into at most one pass per window.
3. Concord stream-key AUTH is no longer blocked behind the user-auth prompt: on a
   relay that hosts our planes, the ASK path is DISMISSed so the derived stream-key
   AUTHs return immediately instead of waiting on (or being dropped by) a dialog.
4. Account.sendMinichatReply evaluates chat.relays() once instead of twice.
5. AuthCoordinator caches the per-stream-key NostrSignerSync by secret, so the
   secp256k1 keypair isn't re-derived for every plane on every relay challenge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 22:15:04 +00:00
Claude 4324a8b8e4 feat(chat): extend dual-mode replies to public chats (NIP-28/NIP-29) — Phase 2
Generalizes the minichat feature beyond Concord to the public chats:

- ChannelNewMessageViewModel gains a replyMode state + toggle; createTemplate now
  builds a kind-1111 CommentEvent for a MINICHAT reply (with the NIP-29 `h` tag on
  relay groups) instead of the native reply, so users can start threads in NIP-28
  public chats and NIP-29 relay groups.
- Account.sendMinichatReply resolves PublicChatChannel and RelayGroupChannel from
  the note's gatherer and publishes a public kind-1111 (h-tagged + host-relay for
  groups), so replying inside a public-chat minichat works too.
- observeNoteMinichatReplyCount re-registers each visible message with the
  EventFinder subscription, which batches their ids into shared REQs for kind-1111
  replies — so the "N replies" chip appears on public-chat messages as their
  threads load. (Concord's replies still arrive over the plane; that REQ no-ops.)
- The reply-mode toggle is extracted to a shared ReplyModeToggle composable used by
  both the Concord and public-chat composers.

The chat-styled minichat screen already generalizes: it reads the root's kind-1111
replies and works for any chat type. DMs (NIP-17) remain the deferred Phase 3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 20:57:39 +00:00
Claude e7ff2744a9 feat(chat): chat-styled minichat screen + route, chip opens it
Adds the "chat within a chat" screen the N-replies chip now opens: the root
message pinned at top, its kind-1111 thread replies below as chat bubbles (reusing
ChatroomMessageCompose so they look identical to the main chat), and a composer
that posts a kind-1111 rooted at that message — flat, so replying inside a minichat
doesn't spawn sub-threads.

- Route.ChatMinichat(rootId) + AppNavigation registration + MinichatScreen.
- The shared "N replies" chip now navigates to the minichat instead of the generic
  thread view.
- Account.sendMinichatReply resolves the chat context from the note's gatherer and
  drives the Concord channel path (kind-1111 on the plane). Observing the root's
  replies also loads kind-1111s from relays, so public-chat minichats already
  display; their send path (NIP-28/NIP-29) is the remaining follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 20:50:52 +00:00
Claude 025d63672f feat(chat): dual-mode replies (inline + minichat) — foundation, Concord composer, shared chip
Adds the two-way reply model: an INLINE reply stays in the timeline (native chat
message referencing its parent), a MINICHAT reply is a kind-1111 NIP-22 comment
pulled into a thread opened from the parent — matching Armada.

Foundation (quartz/commons):
- ReplyMode enum (INLINE default, MINICHAT).
- ChannelChat.inlineReply / ConcordActions.buildChannelInlineReply restore the
  kind-9 q-tag quote path alongside the existing kind-1111 ChannelChat.reply.
- ChannelFeedFilter excludes kind-1111 CommentEvents from the chat timeline (they
  belong in the minichat), so inline replies stay and thread replies move aside.
- observeNoteMinichatReplyCount: local count of a message's kind-1111 replies.

Concord composer + send (amethyst):
- ConcordNewMessageViewModel gains a replyMode state + toggle; the composer shows a
  "In chat" / "In thread" toggle beside the reply preview.
- Account.sendConcordChannelMessage routes MINICHAT to kind-1111, INLINE to kind-9
  quote, fresh post to kind-9 message.

Shared row chip (all chat types):
- ChatMessageCompose's action row shows an "N replies" chip when a message has
  kind-1111 thread replies; tapping opens the thread. Wired to the thread view for
  now; a chat-styled minichat screen and NIP-28/NIP-29 loading follow.

Plan: amethyst/plans/2026-07-12-dual-reply-minichat.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 20:40:29 +00:00
davotoulaandgithub-actions[bot] 8e46714fca chore: sync Crowdin translations and seed translator npub placeholders 2026-07-12 20:06:46 +00:00
Claude 8065b045e4 fix(concord): build thread replies as kind-1111 NIP-22 comments, not kind-9 quotes
Concord replies were kind-9 chat messages carrying a `q` tag. In the Concord
model (matching Soapbox Armada) a `q` on a kind-9 is an inline *quote*, which
clients deliberately keep OUT of threads — a real thread reply is a kind-1111
NIP-22 comment. So our replies rendered (and were sent to Armada) as inline
quotes, never grouping into a message's thread.

ChannelChat.reply now builds a CommentEvent via CommentEvent.replyBuilder: the
uppercase K/E/P tags pin the immutable thread root and the lowercase k/e/p tags
point at the immediate parent (root inherited when the parent is itself a
comment, so the root is stable at any depth), plus the same channel/epoch binding
every Chat Plane rumor carries. This is byte-compatible with Armada's
buildV2CommentTags, so replies thread correctly in both directions. The read path
already accepts these (they carry the binding, and consumeConcordRumor handles
CommentEvent), so incoming Armada thread replies now land bound to their channel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 20:06:20 +00:00
David KasparandGitHub a1f470f1ec Merge pull request #3539 from davotoula/fix/ci-android-oom-and-kover-optin
Remove Kover coverage plugin to fix runner OOM
2026-07-12 21:04:36 +01:00
David KasparandGitHub 8689a409db Merge branch 'main' into fix/ci-android-oom-and-kover-optin 2026-07-12 19:39:56 +01:00
davotoulaandClaude Opus 4.8 d7e163111a build: remove Kover coverage plugin
Reverts the Kover coverage aggregation (introduced in dca345212b) from every
module, the root aggregation block, the Sonar coverage import, the version
catalog, and BUILDING.md. With the plugin present the 16 GB CI runner OOM-killed
during the Android test+build job even with Kover opt-in disabled; removing it
entirely clears the OOM. No other CI changes — the diff versus main is exactly
the inverse of dca345212b.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:52:29 +01:00
Claude 0bac5c7aca fix(concord): populate channel community metadata account-wide so the Messages chip shows
Each Concord channel row in the Messages tab has a chip naming its parent
community (tap → opens the community), mirroring the NIP-29 relay chip — but it
only renders when ConcordChannel.communityName is set, and that was populated
solely by refreshConcordChannelIndex() inside the Concord hub screen's
subscription composable. On the Messages tab that screen isn't mounted, so the
channel objects there never got their community name/icon and the chip was
absent.

Moves the channel-index refresh to an account-scoped collector on the
ConcordSessionManager revision, so community metadata (name/icon, channel flags,
membership) and per-community ban pruning apply across the whole app the moment a
Control Plane folds — not only while the hub screen is open. The hub subscription
now just re-derives its filters; the shared refresh lives in Account.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 15:33:58 +00:00
Claude e9899f95cb feat: remove border from git event cards
Try git patch/issue/PR/repo cards without the 1dp outline so they read
like regular posts; the container keeps only the full-width column.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01USiovhenaijgVEFFzASgfy
2026-07-12 15:20:55 +00:00
Claude 97c7fbfe80 fix: compact git-card action buttons and dedupe issue/PR subject heading
The NIP-34 status buttons (Mark merged / Close / Reopen) and the PR
View-changes button used default Material3 sizing, which reads oversized
inside a note card. They now use the same 32dp compact height, tighter
content padding and labelMedium text as the app-recommendation buttons.

Issue and PR cards also showed the title twice when the authoring client
duplicated the subject tag as a leading markdown heading in the content
(gitworkshop/ngit do this). The body renderer now strips that first
heading when it matches the subject already rendered as the card title.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01USiovhenaijgVEFFzASgfy
2026-07-12 02:59:08 +00:00
Claude bd42b1695b fix(concord): hydrate unsigned channel rumors so messages render (not a placeholder)
Concord channel messages arrived as 6 rows stuck on the 'Event is loading or
can't be found in your relay list' placeholder. A Concord inner rumor is
unsigned (empty sig) and never exists on a relay, but consumeConcordRumor fed it
to justConsume with wasVerified=false, so justVerify ran a signature check,
failed, and the event never loaded onto its Note — the chat row then fell back to
loading-from-relay and showed the placeholder forever.

The rumor's authenticity is already established when the envelope is opened
(ConcordStreamEnvelope.open verifies the seal signature, binds rumor.pubKey ==
seal.pubKey, and checks rumor.verifyId()), exactly like a NIP-59 gift-wrapped DM
rumor. Consume it as pre-verified (wasVerified=true) so the event loads and the
message text renders in the Messages tab alongside other groups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 02:32:17 +00:00
Claude 60475c10c0 feat(concord): NIP-42 AUTH as the derived plane stream key (CORD-01 §4b)
Concord relays gate a plane's kind-1059 wraps behind NIP-42 and serve them only
to a connection authenticated AS the plane's derived stream key — a Concord wrap
is authored by the stream key and p-tagged to a throwaway ephemeral key, so the
member is neither author nor recipient. Amethyst authenticated only as the user,
so every plane REQ was refused ('auth-required: all authors must be
authenticated') and no channel or message ever loaded, even though the community
list (kind 13302) folded correctly.

Amethyst's relay-auth plumbing already supports multiple identities per
connection: RelayAuthenticator sends one AuthCmd per event in the list its
provider returns, deduped on (pubkey, challenge). This wires the derived stream
keys into that provider:

- ConcordCommunitySession.streamKeys() exposes control + folded-channel GroupKeys.
- ConcordSessionManager.streamAuthSecretsFor(relay) returns the stream secret keys
  a NIP-42 challenge from that relay must be answered with (relay-scoped to each
  community's own relays — the same scope the plane subscription uses).
- AuthCoordinator signs one kind-22242 per stream key locally (raw KeyPair via
  NostrSignerSync — never the account signer, never exposing user identity), and
  attaches them to every challenge independent of the user-auth policy. On auth
  success the existing syncFilters re-fires the previously-refused plane REQ.
- RelayAuthStatus LruCaches widened 10 -> 200 since one connection now
  authenticates as the user plus many plane keys (control + channels).

Mirrors Armada's streamAuth.ts (sign one AUTH per scoped stream key, accumulating
on the connection). Verified live on-device by the debug pass: authing as the
derived stream key returns the control-plane wraps that were previously refused.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-12 01:21:58 +00:00
David KasparandGitHub c337c2353a Merge pull request #3540 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-11 23:06:21 +01:00
davotoulaandgithub-actions[bot] 82f1780b55 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 22:01:21 +00:00
David KasparandGitHub 768523143c Merge pull request #3538 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-11 22:59:12 +01:00
davotoulaandgithub-actions[bot] f2f8bdb765 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 16:13:04 +00:00
davotoula 0053edf2dd refactor: adopt androidx.core KTX helpers (Bitmap/Uri/SharedPreferences) 2026-07-11 17:00:40 +01:00
Claude 90998ee838 fix(concord): import the 13302 list from the user's own relays too, not just stock
The community-list import only queried the 4 Concord stock relays, so a list the
user copied onto their own outbox/read relays (or that a client published there)
was never fetched — the normal account subscription never asks for kind 13302.

Widens the query to stock ∪ mineRelays ∪ outboxRelays with a 30s window (stock
relays like relay.ditto.pub can take 10–20s to first response), and logs how many
13302 events were fetched and how many entries decoded so an empty hub is
diagnosable. Renames importConcordCommunitiesFromStockRelays → importConcordCommunities.

Verified live: the account's newest kind-13302 is served without AUTH by the
dreamith stock relay (and, slowly, relay.ditto.pub); the codec, LocalCache
dispatch, and fold path all match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-11 15:25:33 +00:00
David KasparandGitHub 56e4b1ec7d Merge pull request #3537 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-11 16:24:08 +01:00
Claude 3cac9077ec refactor(concord): delete ConcordKinds; kinds live on their owning classes
Removes the last catch-all ConcordKinds constant object. Each remaining kind
now lives on the class that owns it:
- wrap/seal kinds were already ConcordStreamEnvelope.KIND_WRAP/SEAL_* — callers
  (ConcordActions, ConcordSubscriptionPlanner) reference those directly.
- guestbook join/leave (3306) and kick (3309) become Guestbook.KIND_JOIN_LEAVE /
  KIND_KICK.
- direct-invite (3313), voice-presence (23313), and rekey (3303) inline their
  literals on ConcordDirectInvite/VoicePresence/ConcordRekey.

The unused edit/webxdc/typing/snapshot/invite-list/ephemeral-wrap constants are
dropped; they will own their literals when those events get dedicated classes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-11 15:12:32 +00:00
Claude 9c71034dd2 Merge remote-tracking branch 'origin/main' into claude/concord-quartz-amethyst-plan-0oy779 2026-07-11 15:04:57 +00:00
vitorpamplonaandgithub-actions[bot] 757f348a46 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 15:04:41 +00:00
Vitor PamplonaandGitHub 00e02048ea Merge pull request #3536 from davotoula/feat/kover-coverage
Aggregate Kover coverage (for SonarQube import)
2026-07-11 11:02:23 -04:00
Claude 0015b39085 fix(concord): read/write the kind-13302 list in Armada's wire format
Amethyst serialized the joined-community list (kind 13302) as a bare JSON
array of flat camelCase entries, so it could not parse the document Soapbox
Armada actually publishes — a community created in Armada never appeared in
Amethyst even after the 13302 reached a shared relay.

Rewrites the codec to Armada's communityList.ts shape:
{ entries: [ { community_id, seed: JoinMaterial, current: JoinMaterial,
added_at } ], tombstones: [ { community_id, removed_at } ] }, where
JoinMaterial is the snake_case per-snapshot key bundle (community_id, owner,
owner_salt, community_root, root_epoch, channels[], relays, name, held_roots?,
refounder?). Hydration prefers current over seed; liveness is derived from
tombstones (an entry is dropped only when removed strictly after it was added).
New create/join entries now stamp added_at (ms) so the liveness tiebreak works.

Adds interop tests that decode a real Armada document and exercise the
tombstone-after-add drop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-11 14:56:30 +00:00
Claude 127d959c8d refactor(concord): community-list, control, invite-bundle events own their kind literals
Removes the CONTROL/COMMUNITY_LIST/INVITE_BUNDLE constants from ConcordKinds
now that each has a dedicated Event class. Callers reference
ControlEditionEvent.KIND, ConcordCommunityListEvent.KIND, and
ConcordInviteBundleEvent.KIND directly, matching the nip88Polls per-kind
event convention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-11 14:56:28 +00:00
Claude ae6983a7a8 fix(concord): pasting an invite into Search opens the redeem flow, not a broken event screen
Search ran the term through Nip19Parser, which extracted the invite URL's embedded
kind-33301 naddr and routed to the generic addressable-event screen — which has no
renderer for 33301, so it showed 'unable to render kind 33301'. Detect a Concord
invite link before the nip19 parse and route to Route.ConcordInvite (carrying the
whole URL so the fragment token survives). Also guard the bare-naddr path: a 33301
naddr goes to the invite flow instead of the unrenderable event screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-11 14:56:26 +00:00
David KasparandGitHub 4ceb8f78c2 Merge pull request #3532 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-11 15:38:48 +01:00
Vitor PamplonaandGitHub aff10df508 Merge pull request #3535 from vitorpamplona/claude/nostr-metadata-parse-errors-jmzlyl
fix: don't drop whole kind-0 profiles over one mistyped field
2026-07-11 10:38:07 -04:00
davotoula dca345212b build: aggregate Kover coverage for SonarQube import
- Apply Kover (Apache-2.0, build-time only) to the KMP/JVM modules and aggregate at the root
- include amethyst playDebug in the aggregated Kover coverage
2026-07-11 15:37:40 +01:00
Claude 0e1a14a62d fix: don't drop whole kind-0 profiles over one mistyped field
Clients in the wild publish structurally wrong values in profile
metadata — e.g. "nip05":{} — and the strict field serializers made
JsonMapper throw, so contactMetaData() returned null and the entire
profile (name, picture, about…) was discarded.

Generalize the BirthdayTolerantSerializer precedent: string fields now
use TolerantStringSerializer (accepts any JSON primitive, ignores
objects/arrays/null) and the bot flag uses TolerantBooleanSerializer,
so a single malformed field is skipped instead of being fatal.

Non-JSON content (e.g. "Relay initialized") remains unrecoverable and
still parses to null; a test pins that it does so without throwing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSCGp6KgcnaepEMbxNroYe
2026-07-11 14:34:50 +00:00
davotoulaandgithub-actions[bot] 2edb22783c chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 14:32:20 +00:00
davotoula d1e5a4ef83 update cs,sv,de,pt 2026-07-11 15:29:23 +01:00
Vitor PamplonaandGitHub a5f9a234c1 Merge pull request #3534 from vitorpamplona/claude/auth-permission-message-text-moco75
Clarify relay auth descriptions for profile and engagement data
2026-07-11 10:28:13 -04:00
Claude 5365311e7d fix: describe relay auth read permission as profile, posts and engagement
The READ_OUTBOX relay-auth prompt and the settings toggle described the
permission as only loading posts, but authenticating to a follow's relay
also downloads their profile information and post engagement. Update the
prompt title, consequence line, reason label and toggle description to
say so.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012VmzSAtkFv29KWDzPVBbxS
2026-07-11 14:22:09 +00:00
Vitor PamplonaandGitHub 8e38ea6607 Merge pull request #3533 from vitorpamplona/claude/amethyst-miner-performance-0rw1dn
Parallelize PoW mining with multi-worker nonce search
2026-07-11 09:54:01 -04:00
Claude 5683c74629 perf(amethyst): mine on half the device's cores in the Android app
Each PoW job now races PoWPolicy.minerWorkers(cores) = cores/2 parallel
searches (at least 1) instead of a single thread, so a post on an 8-core
phone mines ~4x faster while the other half of the cores stays free for
the UI, and the total CPU budget matches the old 2-job x 1-thread pool.

- PoWPolicy.minerWorkers(availableProcessors) is the single definition of
  the per-job worker budget.
- PoWPublishQueue gains minerThreads (used by its built-in miner and
  exposed for custom mine lambdas); AppModules wires maxConcurrent = 1 +
  minerThreads = half the cores.
- PoWNostrSigner gains a workers param (default 1), covering reactions,
  reposts and reports via Account.miningSigner; the anonymous-post paths
  in the short-note and comment composers pass the same budget.
- Gift-wrap envelope mining stays single-threaded: the template
  conversion is a non-suspend hook inside the synchronous NIP-59 build.
- UI time estimates (settings picker, composer chip, broadcast banner)
  now benchmark at the queue's worker count via deviceHashesPerSecond(),
  and PoWEstimator caches one rate per worker count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhaF5scnAvhP9wr9R7bTWM
2026-07-11 12:13:32 +00:00
Claude bc365c8ca2 refactor(concord): invite bundle (33301) gets its own addressable event
Third refactor slice (plan: quartz/plans/2026-07-11-concord-event-classes.md).
The kind-33301 public invite bundle is now a proper addressable Event class:

- New cord05Invites/bundle/ConcordInviteBundleEvent (BaseAddressableEvent), with a
  build{} template that emits the ['d',''] + ['vsk','6'] tags via the standard DTag
  ext and the shared VskTag (ControlEntityKind.INVITE_LIVE). Registered kind 33301
  -> ConcordInviteBundleEvent in EventFactory so fetched bundles parse as the class.
- ConcordInviteBundle.build now signs ConcordInviteBundleEvent.build(...) with the
  per-link key; the raw TAG_D/TAG_VSK/VSK_LIVE constants are gone. Tag order (d, vsk)
  is preserved so the bundle event id / naddr are unchanged — the cord05Invites
  tests (bundle round-trip + join flow) pass.

Remaining invite kinds: 3313 direct-invite (rumor has empty tags; p/k index is on
the giftwrap) and 13303 invite-list (unimplemented stub) — tracked in the plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig
2026-07-11 12:00:42 +00:00
David KasparandGitHub 9d4e2c3ea6 Merge pull request #3531 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-11 12:15:30 +01:00
davotoulaandgithub-actions[bot] cc275ce62d chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 10:47:38 +00:00
davotoula b8570797fe refactor: extract no-reachability-cache flag name into a constant 2026-07-11 11:44:21 +01:00