A review task assigned to the pr-reviewer rendered as a truncated raw UUID instead of its name. Root cause: the panel resolved assignees from a hardcoded static roster in agent-utils.ts that had drifted — it never gained the board-adjacent agents added backend-side (intake-1, secretary-1, pr-reviewer-1). Their UUIDs hit no map entry, so getAgentDisplayName fell through to the unknown-UUID branch and returned agentId.slice(0, 8). Every assignee surface (task table, task detail, subtasks, journals, communications, commit cards) shares that resolver, so all of them showed the fragment. Make the live /api/agents roster the source of truth instead of a static duplicate that silently rots: - agent-utils: add a runtime registry (registerAgentRoster) keyed by both UUID and slug; resolveToSlug / getAgentDisplayName / isKnownAgent consult it first. The static maps remain only as an offline / first-paint fallback (now complete with the three agents). - api/agents: surface the backend UUID on AgentDefinition (getAll/getOne previously dropped it), so the registry can key by UUID. - use-agents: add useAgentRosterSync (registers the live roster) and derive useAgents from live definitions, falling back to the static roster. - providers: mount the sync once inside QueryClientProvider. Now any agent the backend knows about resolves, including ones added after this change — the panel can no longer drift out of sync. Tests: agent-utils unit tests cover the three agents end-to-end, a live-roster-only agent (drift-proofing), live-overrides-static, and a regression guard for the existing roster.
RoboCo Control Panel
Next.js 16 control panel for the RoboCo AI agent system. Formerly a separate repository (rennf93/roboco-panel), now vendored under panel/ in this monorepo so docker compose up -d brings up the whole stack from one place.
Stack
- Next.js 16 (App Router, standalone output)
- TypeScript
- Tailwind CSS
- Radix UI primitives
dnd-kitfor drag/drop (kanban)- pnpm for package management
Running in production (the normal path)
Use the root-level Docker Compose:
# from the repo root (one level up from this directory)
docker compose up -d
The panel is built as part of the compose stack via docker/panel.Dockerfile and served internally on port 3000. Nginx (also in the compose stack) is the single externally-exposed service on http://localhost:3000 and routes:
/api/*and/ws/*→ orchestrator (FastAPI, port 8000)- everything else → the Next.js panel
The panel uses relative URLs (/api/v1, /ws) so nothing here needs a backend URL in .env.
Running the panel alone for UI development
cd panel
pnpm install
pnpm dev
That gives you Next dev-server on localhost:3000, but you still need the orchestrator reachable at localhost:8000 (or via nginx) for API calls to work. Easiest: docker compose up -d the backend services, then run pnpm dev against that.
Build scripts
pnpm dev— development server with hot reloadpnpm build— production build (outputs.next/standalone/)pnpm start— run the standalone buildpnpm lint— ESLint
Where things live
src/app/— Next.js App Router pagessrc/components/— React components (organized by feature: tasks, agents, channels, …)src/lib/api/— typed API client (thin wrappers overfetch)src/lib/— constants, utilities, WebSocket hookssrc/types/— shared TypeScript types mirroring backend schemas
Backend schema changes
When the backend changes response shapes, mirror them in src/types/ and the relevant src/lib/api/ module. Keep API paths relative so nginx routing keeps working.