Skip to content

refactor(agents): derive avatar once at create, drop reconcile-time backfill#1233

Merged
tellaho merged 1 commit into
kennylopez-agent-avatar-plumbingfrom
tho/agent-avatar-derive-once
Jun 24, 2026
Merged

refactor(agents): derive avatar once at create, drop reconcile-time backfill#1233
tellaho merged 1 commit into
kennylopez-agent-avatar-plumbingfrom
tho/agent-avatar-derive-once

Conversation

@tellaho

@tellaho tellaho commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

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.

Draft / alternative to #1232. This is the derive-once-at-create counter-proposal to the AvatarState tri-state in #1232. Both fix the same bug; we'll land exactly one. Opened as a draft for side-by-side review.

Problem: ManagedAgentRecord.avatar_url: Option<String> overloads None to 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 any None record, 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 (removes resolve_legacy_avatar, the reconcile backfill arm, and the rename-publish .or_else fallback) plus a small symmetric clear path (Option<Option<String>>, mirroring the existing model field).

Contrast with #1232, which instead adds an AvatarState enum + custom serde + an invariant test suite to let reconcile distinguish "never asked" from "cleared." See DERIVE_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_url field (which deserialize as None) render initials until next explicitly set/touched, rather than lazily picking up the command icon. Cosmetic, and confined entirely to the pre-field None population — 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_avatar and the reconcile backfill arm; reconcile_agent_profile's None case is now a plain early-return (no derive, no persist). Dropped the now-dead app param and agent_command/persona_id/pubkey fields from ProfileReconcileData. Create-time resolve_created_avatar_url is 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_changed and 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.rsavatar_url: Option<Option<String>> on the update request (symmetric with model).

desktop/src-tauri/src/managed_agents/restore.rs — Second reconcile call site updated to the new ProfileReconcileData shape.

desktop/src-tauri/src/commands/agents_tests.rs — Dropped the 4 resolve_legacy_avatar tests (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:

  1. Check out tho/agent-avatar-derive-once, run just desktop-tauri-test → 574 lib tests green, clippy -D warnings clean, fmt clean.
  2. Compare this diff against refactor(agents): replace avatar-clear boolean with AvatarState tri-state enum #1232 (tho/agent-avatar-clear-tristate): same bug fixed, opposite shape — net deletion + unchanged data model here vs. enum + serde + new tests there.

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>
@tellaho tellaho force-pushed the tho/agent-avatar-derive-once branch from 3d030e7 to f2f3db7 Compare June 24, 2026 05:52
@tellaho tellaho changed the base branch from main to kennylopez-agent-avatar-plumbing June 24, 2026 05:52
@tellaho tellaho marked this pull request as ready for review June 24, 2026 06:12
@tellaho tellaho merged commit 1d01d88 into kennylopez-agent-avatar-plumbing Jun 24, 2026
42 of 46 checks passed
@tellaho tellaho deleted the tho/agent-avatar-derive-once branch June 24, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant