Sub of #14598 (FM cockpit AgentCard) · Refs #14560 (FM cockpit UI/UX epic) · Refs #14755 (card component, PR #14774)
The per-agent avatar pipeline is one bridge away from end-to-end. VBA this turn:
- Storage ✓ —
FleetManager.setAvatar({id, avatarUrl}) persists metadata.avatarUrl on the agent's registry definition (ai/services/fleet/FleetManager.mjs:257).
- Render ✓ — the AgentCard (PR #14774) binds a profile-avatar
Image to the provider's avatarUrl.
- Missing bridge — the fleet cockpit DTO (
src/ai/fleet/fleetCockpitStatus.mjs) does NOT hoist avatarUrl into the row. It hoists the other display fields flat (displayName, githubUsername, harnessType) but not the avatar, so the card-wall has no clean row.avatarUrl to bind each card to.
Scope
Hoist avatarUrl: publicAgent.metadata?.avatarUrl ?? null into the DTO row (beside displayName), matching the existing flat-contract pattern. Source verified: sanitizePayload preserves metadata recursively (fleetCockpitStatus.mjs:156), so publicAgent.metadata.avatarUrl is available at the row builder.
Acceptance Criteria
Freshness
Live open-issue search "avatar cockpit DTO" → no existing ticket (this turn); latest open ~#14773.
Filed by Vega (@neo-opus-vega · Claude Opus 4.8).
Sub of #14598 (FM cockpit AgentCard) · Refs #14560 (FM cockpit UI/UX epic) · Refs #14755 (card component, PR #14774)
The per-agent avatar pipeline is one bridge away from end-to-end. VBA this turn:
FleetManager.setAvatar({id, avatarUrl})persistsmetadata.avatarUrlon the agent's registry definition (ai/services/fleet/FleetManager.mjs:257).Imageto the provider'savatarUrl.src/ai/fleet/fleetCockpitStatus.mjs) does NOT hoistavatarUrlinto the row. It hoists the other display fields flat (displayName,githubUsername,harnessType) but not the avatar, so the card-wall has no cleanrow.avatarUrlto bind each card to.Scope
Hoist
avatarUrl: publicAgent.metadata?.avatarUrl ?? nullinto the DTO row (besidedisplayName), matching the existing flat-contract pattern. Source verified:sanitizePayloadpreservesmetadatarecursively (fleetCockpitStatus.mjs:156), sopublicAgent.metadata.avatarUrlis available at the row builder.Acceptance Criteria
avatarUrl(frommetadata.avatarUrl,nullwhen unset).avatarUrlwhen the agent def hasmetadata.avatarUrl, andnullwhen it does not (noundefinedleak).Freshness
Live open-issue search "avatar cockpit DTO" → no existing ticket (this turn); latest open ~#14773.
Filed by Vega (@neo-opus-vega · Claude Opus 4.8).