Context
The operator's FM cockpit-as-control-surface reframe (2026-07-02, via @neo-opus-grace) listed profile avatar as one of the per-agent cockpit controls (alongside restart / hooks / wake / repo). #14536 shipped the setRepo verb + the updateAgent registry partial-update primitive; this leaf adds setAvatar — the same fleet-authority definition-field control, following the exact setRepo shape.
The Problem
FLEET_WIRE_METHODS has no way for the cockpit to set an agent's display avatar. An agent definition's metadata is the non-secret free-form field for presentation data, but there is no scoped, pane-reachable verb to mutate just that facet post-definition.
The Architectural Reality
FleetManager thin facade + the updateAgent(id, patch) partial-merge primitive (from #14536) — setAvatar sets metadata.avatarUrl via updateAgent, preserving other metadata.
- Single-
params wire contract (dispatchFleetRequest:30 forwards one arg) — so setAvatar({id, avatarUrl}) is a single payload object, mirroring defineAgent / setRepo.
FleetControlBridge capability allowlist + FLEET_WIRE_METHODS (both wire ends) + the dispatchFleetRequest choke-point (exact-allowlist assertion + routing test) — the full checklist a new wire verb needs (per #14536's RETROSPECTIVE).
- Fleet authority (own-registry mutation, like
defineAgent), NOT control-plane — a display field is not cross-agent-privileged.
The Fix
FleetManager.setAvatar({id, avatarUrl}) → updateAgent(id, {metadata: {avatarUrl}}); single-payload, wire-compatible, non-destructive.
FleetControlBridge.setAvatar(payload) + FLEET_WIRE_METHODS += setAvatar.
- Specs:
FleetManager + FleetControlBridge delegation; dispatchFleetRequest allowlist + routing.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
FLEET_WIRE_METHODS += setAvatar |
operator control-plane reframe; wire SSOT |
pane-reachable on both wire ends + dispatch choke-point |
off-list rejected |
fleetWireMethods JSDoc |
dispatchFleetRequest.spec |
FleetControlBridge.setAvatar(payload) |
capability allowlist |
single-params → FleetManager.setAvatar |
fail-closed |
JSDoc |
FleetControlBridge.spec |
FleetManager.setAvatar({id, avatarUrl}) |
facade + updateAgent |
sets metadata.avatarUrl; null on unknown id |
fail-closed |
JSDoc |
FleetManager.spec |
Decision Record impact
aligned-with ADR-0026 — a fleet-authority operator-UI control verb, no ADR authority change.
Acceptance Criteria
Out of Scope
- Avatar image storage / upload —
avatarUrl is a reference; hosting is a separate concern.
- The cockpit UI — #13448 (Lane B) consumes
registryBridge.setAvatar.
Related
#13015 (parent — FM MVP / Lane C) · #13448 (Lane B cockpit consumer) · #14536 (setRepo sibling — provides updateAgent; this stacks on it) · #14535 · #14537 · ai/services/fleet/FleetManager.mjs
Out-of-repo context
Live latest-open sweep: checked latest 20 open issues 2026-07-03T07:22Z (this session's continuous triage); no setAvatar leaf exists.
Origin Session ID: 29018efb-2edf-4172-902d-0fe42d4b0c11
Retrieval Hint: "Fleet setAvatar metadata.avatarUrl updateAgent FLEET_WIRE_METHODS single payload"
Context
The operator's FM cockpit-as-control-surface reframe (2026-07-02, via @neo-opus-grace) listed profile avatar as one of the per-agent cockpit controls (alongside restart / hooks / wake / repo). #14536 shipped the
setRepoverb + theupdateAgentregistry partial-update primitive; this leaf addssetAvatar— the same fleet-authority definition-field control, following the exactsetReposhape.The Problem
FLEET_WIRE_METHODShas no way for the cockpit to set an agent's display avatar. An agent definition'smetadatais the non-secret free-form field for presentation data, but there is no scoped, pane-reachable verb to mutate just that facet post-definition.The Architectural Reality
FleetManagerthin facade + theupdateAgent(id, patch)partial-merge primitive (from #14536) —setAvatarsetsmetadata.avatarUrlviaupdateAgent, preserving other metadata.paramswire contract (dispatchFleetRequest:30forwards one arg) — sosetAvatar({id, avatarUrl})is a single payload object, mirroringdefineAgent/setRepo.FleetControlBridgecapability allowlist +FLEET_WIRE_METHODS(both wire ends) + thedispatchFleetRequestchoke-point (exact-allowlist assertion + routing test) — the full checklist a new wire verb needs (per #14536's RETROSPECTIVE).defineAgent), NOT control-plane — a display field is not cross-agent-privileged.The Fix
FleetManager.setAvatar({id, avatarUrl})→updateAgent(id, {metadata: {avatarUrl}}); single-payload, wire-compatible, non-destructive.FleetControlBridge.setAvatar(payload)+FLEET_WIRE_METHODS += setAvatar.FleetManager+FleetControlBridgedelegation;dispatchFleetRequestallowlist + routing.Contract Ledger Matrix
FLEET_WIRE_METHODS+=setAvatarfleetWireMethodsJSDocdispatchFleetRequest.specFleetControlBridge.setAvatar(payload)params→FleetManager.setAvatarFleetControlBridge.specFleetManager.setAvatar({id, avatarUrl})updateAgentmetadata.avatarUrl;nullon unknown idFleetManager.specDecision Record impact
aligned-with ADR-0026— a fleet-authority operator-UI control verb, no ADR authority change.Acceptance Criteria
FleetManager.setAvatar({id, avatarUrl})single-payload, setsmetadata.avatarUrlviaupdateAgent,nullon unknown id — unit spec.setAvataradded toFLEET_WIRE_METHODS;dispatchFleetRequestallowlist + routing tests confirm the choke-point routes it without opening resolver seams.FleetControlBridge.setAvatar(payload)on the capability allowlist; secret-omission boundary still holds.Out of Scope
avatarUrlis a reference; hosting is a separate concern.registryBridge.setAvatar.Related
#13015 (parent — FM MVP / Lane C) · #13448 (Lane B cockpit consumer) · #14536 (
setReposibling — providesupdateAgent; this stacks on it) · #14535 · #14537 ·ai/services/fleet/FleetManager.mjsOut-of-repo context
Live latest-open sweep: checked latest 20 open issues 2026-07-03T07:22Z (this session's continuous triage); no
setAvatarleaf exists.Origin Session ID: 29018efb-2edf-4172-902d-0fe42d4b0c11 Retrieval Hint: "Fleet setAvatar metadata.avatarUrl updateAgent FLEET_WIRE_METHODS single payload"