From e2e0fe627166a9b6e541c228468825468d0ae06c Mon Sep 17 00:00:00 2001 From: Duncan Date: Fri, 28 Aug 2026 11:09:44 -0400 Subject: [PATCH] fix(desktop): name the publish notice from the observed persona The publish-catalog success notice reused the instance-oriented agentName, which falls back to the pre-save def.displayName on a definition-only edit (both agent candidates null). A published Alice->Bob rename then toasted "Updated Alice." Derive the notice name from observedPersona.displayName first, separate from the instance-oriented name used by the sync warning and generic toast. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- .../agents/ui/agentSaveCoordinator.test.mjs | 40 +++++++++++++++++++ .../agents/ui/agentSaveCoordinator.ts | 13 +++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/desktop/src/features/agents/ui/agentSaveCoordinator.test.mjs b/desktop/src/features/agents/ui/agentSaveCoordinator.test.mjs index 58bb1ed2b4..5b2eece8b5 100644 --- a/desktop/src/features/agents/ui/agentSaveCoordinator.test.mjs +++ b/desktop/src/features/agents/ui/agentSaveCoordinator.test.mjs @@ -1255,3 +1255,43 @@ test("test_success_toast_uses_observed_name_on_thrown_but_persisted_rename", asy cap.restore(); } }); + +test("test_published_definition_rename_notice_uses_observed_persona_name", async () => { + const cap = captureToasts(); + try { + // Definition-only publish edit: both agent candidates are null, so the + // publish notice must name the DEFINITION from the observed persona, not + // fall back to the pre-save `def.displayName`. + const opts = makeOpts({ + ctx: { + kind: "definition-only", + definition: makeDefinition({ displayName: "Alice" }), + }, + personaInput: makePersonaInput({ displayName: "Bob" }), + publishCatalogUpdates: true, + updatePersonaAndPublish: async () => ({ publicationStatus: "published" }), + refetchStores: async () => ({ + persona: makeDefinition({ displayName: "Bob" }), + agent: null, + }), + }); + + const result = await runAgentSaveCoordinator(opts); + + assert.equal(result, true, "published definition rename is a full success"); + const successes = cap.captured.filter((c) => c.kind === "success"); + assert.equal(successes.length, 1, "exactly one success toast"); + assert.match( + successes[0].message, + /^Updated Bob and published it/, + "publish notice must name the observed (persisted) rename", + ); + assert.doesNotMatch( + successes[0].message, + /Alice/, + "publish notice must not report the stale pre-save definition name", + ); + } finally { + cap.restore(); + } +}); diff --git a/desktop/src/features/agents/ui/agentSaveCoordinator.ts b/desktop/src/features/agents/ui/agentSaveCoordinator.ts index d6f549ffa3..6bd0369d0b 100644 --- a/desktop/src/features/agents/ui/agentSaveCoordinator.ts +++ b/desktop/src/features/agents/ui/agentSaveCoordinator.ts @@ -371,8 +371,17 @@ export async function runAgentSaveCoordinator( if (profileSyncError) { showAgentProfileSyncWarning(agentName, profileSyncError); } else if (publishCatalogUpdates && personaInput) { - // Use personaSaveNotice for the publish-specific success message. - toast.success(personaSaveNotice(agentName, publicationStatus)); + // The publish notice reports the DEFINITION update, so name it from the + // observed persona (disk state) — not `agentName`, which is + // instance-oriented and falls back to the pre-save `def.displayName` on a + // definition-only edit where both agent candidates are null. Fall back to + // the submitted rename, then the pre-save snapshot. + const personaName = + observedPersona?.displayName ?? + personaInput.displayName ?? + def?.displayName ?? + "Agent"; + toast.success(personaSaveNotice(personaName, publicationStatus)); } else { toast.success(`${agentName} saved.`); }