app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 734311bcf65edb3d522b917341c3c3cb72aed186
parent 0d50e6869502fa49dc8b56185aeb9d072df3db47
Author: triesap <tyson@radroots.org>
Date:   Sun,  2 Aug 2026 19:26:57 +0000

core(profile): expose manual active refresh

- add the supported asynchronous active-profile command
- keep signed-out invocation idempotent and network free
- allow repeated refreshes to advance public snapshot revisions
- complete the relay and profile runtime checkpoint ledger

Diffstat:
Mcore/crates/application/src/profile_refresh.rs | 58+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdocs/implementation/nostr-runtime-rcld.md | 6+++---
2 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/core/crates/application/src/profile_refresh.rs b/core/crates/application/src/profile_refresh.rs @@ -7,6 +7,25 @@ use crate::{ }; impl AppCore { + /// Manually refreshes the active account's Nostr kind-0 profile. + /// + /// Cached public metadata remains visible while the asynchronous request is + /// running. Calling this command while signed out is an idempotent no-op. + /// + /// # Errors + /// + /// Returns a safe storage or application-state error. Relay and invalid-data + /// failures are represented as nonfatal snapshot state. + pub async fn refresh_active_profile( + &self, + profiles: &(impl ProfileRepository + ?Sized), + client: &(impl NostrClient + ?Sized), + clock: &(impl Clock + ?Sized), + ) -> Result<AppSnapshot, SafeError> { + self.refresh_profile_for_active_account(profiles, client, clock) + .await + } + /// Refreshes the current active account while retaining any cached profile. /// /// Stale results are discarded when the account is replaced or signed out. @@ -15,7 +34,7 @@ impl AppCore { /// /// Returns a safe storage or application-state error. Relay and invalid-data /// failures are represented as nonfatal snapshot state. - pub async fn refresh_profile_for_active_account( + async fn refresh_profile_for_active_account( &self, profiles: &(impl ProfileRepository + ?Sized), client: &(impl NostrClient + ?Sized), @@ -365,4 +384,41 @@ mod tests { Some("Cached") ); } + + #[tokio::test] + async fn manual_refresh_is_repeatable_and_signed_out_safe() { + let profiles = MemoryProfiles::default(); + let (core, public_key) = active_core(&profiles, None); + let first = core + .refresh_active_profile( + &profiles, + &FixedClient(Ok(Some(profile(public_key, "First", 10)))), + &FixedClock, + ) + .await + .expect("first refresh"); + let second = core + .refresh_active_profile( + &profiles, + &FixedClient(Ok(Some(profile(public_key, "Second", 20)))), + &FixedClock, + ) + .await + .expect("second refresh"); + + assert!(second.revision() > first.revision()); + assert_eq!( + second + .active_account() + .and_then(|active| active.profile()) + .and_then(ProfileMetadata::name), + Some("Second") + ); + let signed_out = core.sign_out().expect("sign out"); + let no_op = core + .refresh_active_profile(&profiles, &FixedClient(Ok(None)), &FixedClock) + .await + .expect("signed-out no-op"); + assert_eq!(no_op, signed_out); + } } diff --git a/docs/implementation/nostr-runtime-rcld.md b/docs/implementation/nostr-runtime-rcld.md @@ -362,7 +362,7 @@ and workspace tests. ### RCLD-08: Relay and profile runtime -Status: pending. +Status: completed. Scope: checkpoints 36 through 40. Add environment relay configuration, exact WebSocket policy, verified kind-0 parsing, deterministic local relay fixture, @@ -517,7 +517,7 @@ handoff commit sequence. - [x] 37. Implement Nostr event verification and kind-0 parsing adapter. - [x] 38. Add Nostr client port implementation and local relay fixture. - [x] 39. Implement cache-first active profile refresh orchestration. -- [ ] 40. Expose manual refreshActiveProfile command. +- [x] 40. Expose manual refreshActiveProfile command. ### RCLD-09 @@ -564,7 +564,7 @@ handoff commit sequence. - [x] RCLD-05: Credential boundary. - [x] RCLD-06: Account generation and import. - [x] RCLD-07: Account lifecycle and recovery. -- [ ] RCLD-08: Relay and profile runtime. +- [x] RCLD-08: Relay and profile runtime. - [ ] RCLD-09: UniFFI and native build integration. - [ ] RCLD-10: Thin Kotlin shell and minimal UI. - [ ] RCLD-11: End-to-end integration hardening.