Persona rename leaves linked instances on the old relay identity, and the rename guard makes it unrecoverable
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
In `desktop-v0.5.10`, a persona-linked agent instance can end up with a `name` that differs from its persona's `display_name`. When that happens:
1. The Desktop UI shows the new name and the agent's system prompt uses the new name, so the rename looks successful.
2. The relay identity (kind:0 `display_name`) is republished from `record.name` on **every agent start**, so the published name silently reverts to the old one.
3. `@mention` resolution runs against the relay member list, so mentioning the agent by the name the UI shows fails to resolve and the message is never delivered.
4. **There is no recovery path in the app.** Renaming again cannot fix it, because the propagation helper skips exactly the records that have diverged.
Observed on a real workspace: three builtin-persona agents renamed on 2026-08-10 kept publishing `Fizz` / `Honey` / `Bumble` two days later, while their definitions, prompts and sidebar entries all read `Monkey2` / `Dummy2` / `Goofus2`. Only a hand-edit of `managed-agents.json` fixed it.
## Root cause
### 1. The rename guard makes divergence permanent
[`propagate_persona_name_rename`](https://github.com/block/buzz/blob/desktop-v0.5.10/desktop/src-tauri/src/commands/personas/update.rs#L36-L53) only renames instances whose current `name` equals the persona's **old** `display_name`:
```rust
if record.name != old_display_name {
continue; // pool-named instance — keep its individualised name
}
```
The intent is sound — pool-named instances (`Birch`, `Compass`) should keep their individual names. But the guard is also the only thing standing between a diverged record and repair, and the condition it tests **can never become true again** once `record.name` and `persona.display_name` differ for any reason. Each subsequent rename moves `old_display_name` further away. The record is stranded permanently.
There is no UI to edit a persona-linked instance's `name` directly (same limitation noted for `acp_command` in #5508), so the owner's only fix is editing `managed-agents.json` by hand with the app quit.
### 2. Relay-synced persona renames don't propagate at all
[`apply_inbound_persona`](https://github.com/block/buzz/blob/desktop-v0.5.10/desktop/src-tauri/src/commands/personas/inbound.rs#L342-L364) overwrites `local.display_name` and `local.system_prompt` from an inbound kind:30175 event, but — unlike `update_persona` — never calls `propagate_persona_name_rename` and never touches linked instances:
```rust
local.display_name = inbound.display_name;
local.avatar_url = inbound.avatar_url;
local.system_prompt = inbound.system_prompt;
```
So a rename performed on device A and synced to device B updates B's definition and prompt while B's instance keeps its old `name`. That is a divergence created by sync alone, with no user error involved — and by defect 1, device B can then never repair it through the UI. This looks like the same ownership problem as #5218, seen from the naming side.
### 3. Startup reconciliation actively overwrites any manual correction
`start_managed_agent` builds the reconcile payload straight from the instance record ([agents.rs#L1114-L1123](https://github.com/block/buzz/blob/desktop-v0.5.10/desktop/src-tauri/src/commands/agents.rs#L1114-L1123)):
```rust
let reconcile = ProfileReconcileData {
name: record.name.clone(),
...
```
and [`profile_needs_sync`](https://github.com/block/buzz/blob/desktop-v0.5.10/desktop/src-tauri/src/commands/agents_profile.rs#L167-L180) treats *any* relay name that differs from `record.name` as stale and republishes.
This is correct behaviour given a correct `record.name`, but it means a stale instance name is not merely unpublished — it is re-asserted over a correct relay profile on every single start. An owner who repairs the relay profile out-of-band (`buzz users set-profile --name`) sees the fix silently reverted the next time the agent boots.
## Evidence from the affected workspace
`managed-agents.json`, `desktop-v0.5.10`, macOS 26.6.1. Definition rows carry `pubkey: ""`; instance rows carry the pubkey and `persona_id`.
| persona_id | definition `display_name` | instance `name` | instance `display_name` |
|---|---|---|---|
| `builtin:fizz` | Monkey2 | **Fizz** | *absent* |
| `builtin:honey` | Dummy2 | **Honey** | *absent* |
| `builtin:bumble` | Goofus2 | **Bumble** | *absent* |
| `2c586e69-…` (never renamed) | Mrs Altman | Mrs Altman | — |
| `e3c3724a-…` (never renamed) | PSSOXBot | PSSOXBot | — |
| `216103d5-…` (never renamed) | youngbot | youngbot | — |
The absent instance `display_name` is the diagnostic: `propagate_persona_name_rename` sets `record.name` **and** `record.display_name` together ([update.rs#L50-L51](https://github.com/block/buzz/blob/desktop-v0.5.10/desktop/src-tauri/src/commands/personas/update.rs#L50-L51)). Neither is set on any of the three, so the helper never ran for them — consistent with the guard skipping all three.
`system_prompt` propagated correctly in all six cases (all three renamed agents run prompts that open *"You are Monkey2 / Dummy2 / Goofus2"*), which is why the rename looks complete from inside the app.
Startup republish, three for three:
| agent | last start | relay `display_name` after |
|---|---|---|
| `builtin:fizz` | 2026-08-12 19:04:28Z | `Fizz` — overwrote a manual correction made 2 days earlier |
| `builtin:bumble` | 2026-08-12 21:01:19Z | `Bumble` — same |
| `builtin:honey` | 2026-08-10 20:32:18Z (not restarted since) | `Dummy2` — manual correction survived |
After hand-editing the three instance `name` fields with the app quit, `builtin:fizz` restarted at 2026-08-12 21:28:37Z and the relay profile stayed `Monkey2`.
## Reproduction
Direct repro of defect 1 (no sync required):
1. Create an agent from a persona, then make `record.name` differ from `persona.display_name` by any route — hand-edit, sync, or pool naming.
2. Rename the persona in Desktop.
3. The sidebar and system prompt update; `record.name` does not.
4. Start the agent. The relay kind:0 `display_name` is republished with the old name.
5. Rename again to try to fix it. It still does not propagate — the guard now compares against an even older name.
Repro of defect 2: rename a persona on device A, let the kind:30175 event reach device B, inspect B's `managed-agents.json` — the definition is renamed, the instance is not.
## Likely origin in this case (hypothesis)
The owner migrated to a new laptop and imported their previous setup. A plausible chain that matches every timestamp on disk: the fresh install seeded builtin instances with the code defaults (`Fizz` / `Honey` / `Bumble`, definitions created `16:25:13Z`, instances `16:35:15Z`), an imported/synced definition then set the persona names to the values from the old machine, and the owner's UI rename ~4 hours later (`20:29Z`) compared `record.name = "Fizz"` against an `old_display_name` that was no longer `Fizz` — so the guard skipped it.
I could not confirm which write path ran on 08-10; that state is gone. The two code-level defects above stand on their own regardless of how the initial divergence was introduced.
## Suggested direction
Not prescribing the ownership model — #5328 and #5508 are already circling it — but three things would each have prevented this independently:
- **Distinguish "pool-named" from "diverged" explicitly** rather than inferring it from name equality. If instances recorded whether their name was individualised (pool-drawn/user-set) or inherited from the definition, the guard would be a flag check and inherited names would always follow a rename.
- **Give `apply_inbound_persona` the same propagation as `update_persona`**, so a synced rename cannot create the divergence in the first place.
- **Surface the divergence.** An agent whose published relay identity differs from the name shown in the UI is user-visible breakage — mentions to it fail — and should not be silent. A stale-name badge, or an editable instance name for persona-linked agents, would give owners a recovery path that is not "quit the app and edit JSON".
Contributor guide
Assessment
This issue has not been assessed yet.