diff --git a/desktop/src-tauri/src/commands/agents_pending.rs b/desktop/src-tauri/src/commands/agents_pending.rs
index 0a7f91eb854..05ca7bf0c78 100644
--- a/desktop/src-tauri/src/commands/agents_pending.rs
+++ b/desktop/src-tauri/src/commands/agents_pending.rs
@@ -22,8 +22,8 @@ use crate::{app_state::AppState, managed_agents::ManagedAgentRecord};
/// only runtime fields produces an identical row and never re-enqueues a
/// publish. Best-effort: a failure here is logged and swallowed so a retention
/// hiccup never blocks the disk-authoritative write.
-pub(crate) fn retain_managed_agent_pending(
- app: &AppHandle,
+pub(crate) fn retain_managed_agent_pending(
+ app: &tauri::AppHandle,
state: &AppState,
record: &ManagedAgentRecord,
) {
diff --git a/desktop/src-tauri/src/commands/personas/mod.rs b/desktop/src-tauri/src/commands/personas/mod.rs
index ac43a4719ab..300d3180dc3 100644
--- a/desktop/src-tauri/src/commands/personas/mod.rs
+++ b/desktop/src-tauri/src/commands/personas/mod.rs
@@ -66,6 +66,10 @@ pub(super) use pending::tombstone_persona_pending;
mod create;
pub use create::create_persona;
mod sharing;
+#[cfg(test)]
+pub(crate) use pending::{prepare_persona_publication_at, PreparedPersonaPublication};
+#[cfg(test)]
+pub(crate) use sharing::publish_and_refresh_teams_at;
pub use sharing::set_persona_shared;
pub use sharing::update_persona_and_publish;
mod update;
diff --git a/desktop/src-tauri/src/commands/personas/pending.rs b/desktop/src-tauri/src/commands/personas/pending.rs
index 3e4fabbcf5b..6bdcc7602ec 100644
--- a/desktop/src-tauri/src/commands/personas/pending.rs
+++ b/desktop/src-tauri/src/commands/personas/pending.rs
@@ -10,7 +10,7 @@ use crate::managed_agents::{
AgentDefinition,
};
-pub(super) struct PreparedPersonaPublication {
+pub(crate) struct PreparedPersonaPublication {
pub scope: RetentionScope,
pub event: nostr::Event,
pub retained: RetainedEvent,
@@ -64,8 +64,8 @@ pub(in crate::commands) fn retain_persona_pending_at(
/// exact share tag. The explicit share toggle passes `Some(shared)`. Returning
/// the retained event lets that command immediately await relay acceptance
/// without rebuilding or re-signing a different NIP-33 head.
-pub(super) fn prepare_persona_publication(
- app: &AppHandle,
+pub(super) fn prepare_persona_publication(
+ app: &AppHandle,
state: &AppState,
persona: &AgentDefinition,
shared_override: Option,
@@ -104,8 +104,8 @@ fn retained_persona_is_shared(row: Option<&RetainedEvent>) -> bool {
/// never present an unshared persona as published. The durable share state
/// lives in the retention head, so nothing is lost: the true value reappears
/// once the identity is signable again.
-pub(super) fn project_active_persona_sharing(
- app: &AppHandle,
+pub(super) fn project_active_persona_sharing(
+ app: &tauri::AppHandle,
state: &AppState,
personas: &mut [AgentDefinition],
) {
@@ -156,7 +156,7 @@ fn project_persona_sharing_at(
Ok(())
}
-pub(super) fn prepare_persona_publication_at(
+pub(crate) fn prepare_persona_publication_at(
db_path: &std::path::Path,
keys: &nostr::Keys,
persona: &AgentDefinition,
diff --git a/desktop/src-tauri/src/commands/personas/sharing.rs b/desktop/src-tauri/src/commands/personas/sharing.rs
index 331ec9d0d70..8146f98580b 100644
--- a/desktop/src-tauri/src/commands/personas/sharing.rs
+++ b/desktop/src-tauri/src/commands/personas/sharing.rs
@@ -5,12 +5,25 @@ use crate::{
managed_agents::{
load_personas,
retention::{mark_synced, open_retention_db},
+ storage::managed_agents_base_dir,
AgentDefinition,
},
};
use super::pending::{prepare_persona_publication, PreparedPersonaPublication};
+/// Test-only observer called immediately after the `managed_agents_store_lock`
+/// is acquired in `publish_and_refresh_teams_at`'s refresh section. Tests use
+/// this to assert `try_lock()` fails — proving the lock is held during the
+/// synchronous refresh. Moving the lock acquisition to AFTER the refresh call
+/// (recreating the TOCTOU race) causes `try_lock()` to succeed, turning the
+/// probe test RED.
+#[cfg(test)]
+type RefreshLockObserver = Box;
+#[cfg(test)]
+pub(crate) static REFRESH_LOCK_OBSERVER: std::sync::Mutex
+ {/* Start on app launch (L-field) */}
+ {onStartOnAppLaunchChange ? (
+
+
+
+ {startOnAppLaunch
+ ? "This agent starts automatically when you open Buzz."
+ : "Start this agent manually from the profile panel."}
+
+
+ ) : null}
+
{/* Agent runtime args */}