refactor(agents): derive avatar once at create, drop reconcile-time backfill#1233
Merged
tellaho merged 1 commit intoJun 24, 2026
Merged
Conversation
Restack derive-once onto the #1202 avatar-plumbing branch and subtract #1202's avatar-clear mechanism. The avatar is a command-derived default icon, so it is derived once at create (resolve_created_avatar_url) and stored as a plain Option<String>; there is no reconcile-time backfill to guard against. Removed: - avatar_url_cleared: bool field and all initializer/test sites - profile_sync_avatar_url .or_else(managed_agent_avatar_url) re-derive - dead Fizz-retirement / legacy-recovery / command-fallback helpers - now-unused app: &AppHandle param on reconcile_agent_profile Leaves #1202's non-avatar plumbing intact (persona/team model fields, app-avatar: prefix parsing, imported app-avatar handling). All three icon-resurrection vectors are now gone. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
3d030e7 to
f2f3db7
Compare
1d01d88
into
kennylopez-agent-avatar-plumbing
42 of 46 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1202 (base branch:
kennylopez-agent-avatar-plumbing). This PR supersedes #1202's avatar-clear story; the non-avatar plumbing in #1202 stays as-is.Category: refactor
User Impact: A managed-agent avatar that a user clears now stays cleared — it can no longer be silently resurrected by a background reconcile or a later rename.
Problem:
ManagedAgentRecord.avatar_url: Option<String>overloadsNoneto mean two contradictory things — "never set, go derive the runtime default icon" and "user deliberately cleared it." Because the reconcile pass (and a couple of publish paths) re-derive the default for anyNonerecord, a user who clears their agent's avatar gets it resurrected on the next reconcile or rename. Agent avatars today are always command-derived default icons (goose / Claude / Codex brand badges) — there is no user-uploaded image and no shipped UI to set a custom one — so in practice the only user-reachable action is clear, and it doesn't stick.Solution: Fix the ambiguity at the root rather than guarding it with a new state. Resolve the default icon once at create time and store a concrete value; do no reconcile-time re-derivation. Then
None/empty unambiguously means "no avatar, show initials," and nothing exists to resurrect a clear. The data model is unchanged (Option<String>) — this PR is a net deletion (removesresolve_legacy_avatar, the reconcile backfill arm, and the rename-publish.or_elsefallback) plus a small symmetric clear path (Option<Option<String>>, mirroring the existingmodelfield).Contrast with #1232, which instead adds an
AvatarStateenum + custom serde + an invariant test suite to let reconcile distinguish "never asked" from "cleared." SeeDERIVE_ONCE_AVATAR_SKETCH.md(included in this diff) for the full per-path behavior and graded tradeoffs.Honest tradeoff: with reconcile-backfill gone, legacy records created before the
avatar_urlfield (which deserialize asNone) render initials until next explicitly set/touched, rather than lazily picking up the command icon. Cosmetic, and confined entirely to the pre-fieldNonepopulation — nothing affects a freshly-created agent. Two smaller follow-ons (relay-profile drift no longer self-heals the avatar; no one-time persona recovery on migration) are documented in the sketch note, same population.Scope: avatar state model only. The
app-avatar:<id>symbolic-ref scheme is untouched. The TS UI wiring (UpdateManagedAgentInput.avatarUrl) so a user can actually trigger a clear is a separate follow-up.File changes
desktop/src-tauri/src/commands/agents.rs — Deleted
resolve_legacy_avatarand the reconcile backfill arm;reconcile_agent_profile'sNonecase is now a plain early-return (no derive, no persist). Dropped the now-deadappparam andagent_command/persona_id/pubkeyfields fromProfileReconcileData. Create-timeresolve_created_avatar_urlis unchanged — the one place the command default is derived.desktop/src-tauri/src/commands/agent_models.rs — Added avatar-clear plumbing; publish fires on
name_changed || avatar_changedand uses the stored avatar verbatim. Removed the.or_else(managed_agent_avatar_url)re-derive fallback (the rename-publish resurrection vector).desktop/src-tauri/src/managed_agents/types.rs —
avatar_url: Option<Option<String>>on the update request (symmetric withmodel).desktop/src-tauri/src/managed_agents/restore.rs — Second reconcile call site updated to the new
ProfileReconcileDatashape.desktop/src-tauri/src/commands/agents_tests.rs — Dropped the 4
resolve_legacy_avatartests (helper no longer exists).DERIVE_ONCE_AVATAR_SKETCH.md — Design note: premise, per-path behavior, and the three graded tradeoffs. (Sketch artifact — flagging for tho whether to keep in the shipped diff or strip before merge.)
Reproduction:
tho/agent-avatar-derive-once, runjust desktop-tauri-test→ 574 lib tests green, clippy-D warningsclean, fmt clean.tho/agent-avatar-clear-tristate): same bug fixed, opposite shape — net deletion + unchanged data model here vs. enum + serde + new tests there.