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:
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.