feat: prepare 0.9.0-alpha.0 — avatar system, fleet improvements, footer, tests#31
Merged
simonCatBot merged 40 commits intomasterfrom Apr 7, 2026
Merged
feat: prepare 0.9.0-alpha.0 — avatar system, fleet improvements, footer, tests#31simonCatBot merged 40 commits intomasterfrom
simonCatBot merged 40 commits intomasterfrom
Conversation
…election - Add AvatarSource type (auto | default | custom) and defaultAvatarIndex to AgentStoreSeed/AgentState - Add AvatarSelector component with 3-tab UI (Auto/Default/Custom) for agent creation - Update AgentAvatar to render all 3 sources with buildDefaultAvatarUrl helper - Update AgentCreateModal to use AvatarSelector replacing single Shuffle button - Add footer avatar mode toggle (auto/default) with localStorage persistence - Extend ROCclawSettings schema with avatarSources for persistent per-agent config - Wire persistAvatarConfig through creation flow and shuffle handler - Update hydration to restore avatarSource/defaultAvatarIndex from settings - Add 6 default profile images to public/avatars/ - Update all affected tests
…ion editing - Add onAvatarChange prop to AgentBrainPanel (personality tab) - Replace avatar text input with AvatarSelector component in identity section - Add handleUpdateAgentAvatar to mutation controller (update-agent-avatar kind) - Add gatewayUrl + schedulePatch to mutation controller params for local settings persistence - Wire onAvatarChange → handleUpdateAgentAvatar in page.tsx
Replace the old toggle button with a proper dropdown menu styled like ColorSchemeToggle. Shows current mode icon in the footer and opens a picker with 3 options and descriptions. New files: - AvatarModeToggle.tsx — dropdown with auto/default/custom modes Updated: - FooterBar.tsx — uses AvatarModeToggle, reads mode from localStorage directly
- Remove unused AvatarSelectorHandle import and avatarRef - Add AgentInspectPanels.tsx to eslint exception for setState in effect - Switch useEffect to useLayoutEffect for avatar value sync
Moved AvatarModeToggle out of conditional blocks so it appears regardless of agent state, placed directly beside ColorSchemeToggle in the right-most section of the footer bar.
…n sync AvatarModeToggle was managing state in isolation, so FooterBar was reading stale localStorage values and never re-rendering on mode change. Solution: lift state into AvatarModeContext (AvatarModeProvider) so both AvatarModeToggle and FooterBar read from the same React state. Added Providers wrapper in layout.tsx for the provider. FooterBar now calls useAvatarMode() reactively. New files: - AvatarModeContext.tsx — shared state + provider - Providers.tsx — client-side provider wrapper Updated: - AvatarModeToggle.tsx — reads/writes via context - FooterBar.tsx — uses useAvatarMode() hook - layout.tsx — wraps app in AvatarModeProvider
AgentAvatar (used in fleet tiles, agent header, etc.) now calls useAvatarMode() internally. The global footer toggle now propagates to every AgentAvatar instance throughout the app, not just FooterBar.
… different images When in 'default' mode, deriveDefaultIndex uses a hash of the agent's seed string to pick an avatar — so every agent gets a distinct image even if they all have defaultAvatarIndex=0. Explicitly set indices (via Cycle or grid click) still override. Also added: - UNSET_AVATAR_INDEX sentinel (-1) to distinguish 'never explicitly set' from 'user chose index 0' - Cycle button in AvatarSelector default tab (replaces index badge) - buildDefaultAvatarSelectorValue seeds with UNSET_INDEX
…ique default images on reload deriveDefaultIndex now always combines seed hash + explicitIndex so different agents always get different images even if all have defaultAvatarIndex=0.
…torage read Avoid calling setState directly inside useEffect when the initial value can be computed synchronously at render time.
TasksDashboard was always calling buildAvatarDataUrl directly for agent avatars, completely bypassing AgentAvatar and the AvatarModeContext. Now: - agentAvatarSrc() accepts footerMode and derives index from agentId - useAvatarMode() called in TasksDashboard and AgentFilterChips - footerMode threaded through CronJobTile, RunTile, SortableCronJobTile, TaskDetailPanel, and all JSX usages - default mode shows default avatars in task/agent filter chips too
AgentAvatar now supports size="fill" which uses Next.js fill + object-cover to scale to the parent container. FleetSidebar uses flex-1 on the avatar container so it expands to fill all available space above the text. Also added object-cover class to AgentAvatar Image for better aspect handling.
AgentAvatar with size=fill now uses relative positioning and object-cover so the image properly fills and centers within its container.
…an template - Avatar container now uses flex items-center justify-center for true centering - Removed redundant soul name conditional; identityName ?? name is always used - Clean badge row: model + status only, no double agent name - Removed unused getSoulName helper
identityName now checks agent.identity?.name first (from API) before agent.identityName (fallback). This correctly shows the soul name set in Agent Settings → Behavior → Identity → Name. Also increased card min-width from 200px to 240px so model names fit on one line.
The AgentsListResult type in agentFleetHydration.ts was missing identityName and identityEmoji fields that are present in the derivation file. This ensures soul names from Agent Settings properly flow through the agents.list response.
Gateway agents.list returns identity: { name } not flat identityName.
Removing flat fields from AgentsListResult type — derivation already
correctly uses agent.identity?.name as primary source.
Clean up all flat identityName/identityEmoji fields — gateway only
returns nested identity: { name }. Now consistently reads from
agent.identity?.name across both hydration files.
Gateway may return flat identityName field alongside nested identity.name. Priority: identity?.name (nested) || identityName (flat) || null. This restores the original logic that checked both sources.
After agents.list, fetch IDENTITY.md for each agent in parallel batches of 6. parsePersonalityFiles extracts identity.name/emoji which now flows through deriveHydrateAgentFleetResult into AgentStoreSeed.identityName. FleetSidebar now correctly shows soul names (Kapu, Simon, etc.) instead of repeating the agent ID.
added 4 commits
April 3, 2026 18:53
1. Soul names in fleet cards: fetch IDENTITY.md via HTTP API for each agent during hydration. Parse identity.name directly since the file API only returns one file at a time. This correctly shows Kapu, Frank, Peter etc. in fleet cards. 2. TasksDashboard avatar divisor: was hardcoded to 6, changed to DEFAULT_AVATAR_COUNT (12) so correct profile images show. 3. Footer AvatarModeToggle icon: replaced gradient dot with Smile icon from lucide-react for auto/default modes.
TasksDashboard was using a different hash formula (| 0) than AgentAvatar (& 0xffffffff), causing developer agent to show profile-3.png in task tiles but profile-11.png in fleet cards. Export deriveDefaultIndex from AgentAvatar and reuse it in TasksDashboard so both compute the same index for all agents.
FooterBar was passing defaultAvatarIndex directly to buildDefaultAvatarUrl, skipping the seed-hash derivation. This made footer avatars differ from fleet/card avatars in default mode. Now threads avatarSeed through deriveDefaultIndex like AgentAvatar and TasksDashboard do.
Unit tests (4 new files): - avatarModeContext: AvatarModeProvider, useAvatarMode, useSetAvatarMode, localStorage persistence, round-trip state transitions - agentAvatar: deriveDefaultIndex (determinism, range, edge cases), buildDefaultAvatarUrl (wrapping, profile paths) - avatarSelector: renders tabs/shuffle, calls onChange for all three modes (auto/default/custom), calls shuffle with new seed - rocclawSettings: normalizeGatewayKey, normalizeROCclawSettings, mergeROCclawSettings, resolveFocusedPreference, resolveAgentAvatarSeed, resolveAgentAvatarConfig E2E tests (2 new files, 8 tests): - avatar-toggle: open dropdown, switch modes, persistence across reload, toggle present with empty fleet - agent-avatar-settings: fleet row click → selected class, triggers focused PUT with selectedAgentId, shuffle triggers avatar PUT Bug fix: pre-existing studioSettingsRoute test had insufficient timeout (5000ms) causing WebSocket ENOTFOUND to time out; raised to 30000ms.
5 tasks
added 6 commits
April 6, 2026 15:41
- avatarModeContext: add @ts-expect-error for null localStorage mock - avatarSelector: import AvatarSelectorValue type, remove duplicate test, use screen.getAllByRole('img') instead of data-avatar-index attribute - studioSettingsRoute: remove broken testTimeout reference that CI doesn't support (pre-existing flaky test — unrelated to these changes)
Clicking the Default tab fires onChange (tab switch), so filter calls by avatar index rather than asserting exact call count. Use container.querySelector for grid buttons since they aren't accessible via getByRole in jsdom.
Merge with origin/master clobbered: - package.json version back to 0.1.0 (restored to 0.9.0-alpha.0) - CHANGELOG.md (created fresh with all changes since 0.1.0) - README.md alpha banner and version badge
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.
rocCLAW v0.9.0-alpha.0 — Alpha Preview Release
What this PR does
Version bump:
package.json→0.9.0-alpha.0with alpha description and keywordsNew
CHANGELOG.md:0.1.0through the currentfeat/avatar-customizationbranchAvatar system (auto/default/custom):
AvatarModeContext— global state for footer + fleet avatar mode toggleAvatarModeToggle— dropdown with Auto/Default/Custom modes, persists to localStorageAgentAvatar— derives per-agent avatar fromAvatarModeContext; supports auto (multiavatar lib), default (12 PNG profiles), and custom URL modesAvatarSelector— in-agent settings panel with shuffle button and per-mode selectionderiveDefaultIndex— deterministic index fromseed + explicitIndexfor consistent defaultsFleet sidebar improvements:
IDENTITY.mdper agentAgentSettingsMutationController— batched optimistic settings updatesderiveDefaultIndexAlpha disclaimers on all docs:
New tests (6 files, +839 lines):
tests/unit/avatarModeContext.test.tstests/unit/agentAvatar.test.tstests/unit/avatarSelector.test.tstests/unit/rocclawSettings.test.tstests/e2e/avatar-toggle.spec.tstests/e2e/agent-avatar-settings.spec.tsChecklist
This PR merges
feat/avatar-customization→masterto prepare the first alpha preview release.