Files
roboco/panel
73c05cfa5e feat(a2a): CEO can DM the Auditor and PR reviewers (#623)
* feat(a2a): CEO can DM the Auditor and PR reviewers

A mid-flight PR reviewer or Auditor that's stuck was unreachable — the CEO
had no way to DM them. Both roles now carry dm/read_a2a, so the CEO can open
a 1:1 and they can reply in-thread through the existing CEO-reply path.

Scoped deliberately: the Auditor stays a silent observer to its peers — it
gains no peer-initiation surface (can_a2a_direct routes it through
_check_auditor_a2a, which refuses every initiation target; it can only reply
inside a CEO-opened DM). PR reviewers keep their owning-PM scope. Intake and
Secretary stay excluded — they have their own dedicated chat pages.

NO_COMMS_ROLES drops to {prompter, secretary}; the panel's EXCLUDE_NON_DM_ROLES
matches. KB/docs updated so the 'auditor/pr_reviewer have no dm' claim isn't
left stale.

* test(a2a): smoke guard checks _NO_COMMS_ROLES, not a hardcoded 'auditor'

The dm() runtime guard no longer names the auditor (it now carries dm to
reply to the CEO); it refuses the canonical _NO_COMMS_ROLES set. Assert on
that set so the smoke test tracks the guard, not a stale role name.

* chore(foundation): regenerate verb tables for auditor/pr_reviewer dm+read_a2a

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-21 05:54:29 +02:00
..
2026-07-02 15:36:49 +02:00
2026-04-20 15:10:54 +02:00
2026-06-29 05:38:21 +02:00
2026-05-09 03:15:09 +02:00

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-kit for 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 reload
  • pnpm build — production build (outputs .next/standalone/)
  • pnpm start — run the standalone build
  • pnpm lint — ESLint

Where things live

  • src/app/ — Next.js App Router pages
  • src/components/ — React components (organized by feature: tasks, agents, channels, …)
  • src/lib/api/ — typed API client (thin wrappers over fetch)
  • src/lib/ — constants, utilities, WebSocket hooks
  • src/types/ — shared TypeScript types mirroring backend schemas (includes NotificationType enum)
  • src/hooks/ — reusable React hooks (see Frontend hooks)
  • src/app/(dashboard)/notifications/ — notifications inbox page and components

Hooks

The panel exposes public hooks under @/hooks. See Frontend hooks for full API reference and examples.

usePageRefresh

Page-scoped refresh coordinator. Pages register data-refetch callbacks; the navbar refresh button in src/components/layout/header.tsx calls refresh() and reflects the combined loading/disabled state. The button is disabled when no callbacks are registered (the registry is empty) and while a refresh is in progress.

import { usePageRefresh } from "@/hooks";

const { register, unregister, refresh, loading, disabled } = usePageRefresh();
  • disabled is true when no callbacks are registered (there is nothing to refresh)
  • disabled becomes false once a callback is registered
  • disabled returns to true when all callbacks are unregistered

Wrap your page or layout in PageRefreshProvider from @/components/providers before consuming the hook. Dashboard pages should register their refetch callbacks and avoid adding inline "Refresh" buttons; see docs/frontend/components/page-refresh-provider.md for the full wiring list and examples.

Notifications

The panel renders five core coordination-event notification types that signal task lifecycle transitions between agents:

Type Icon Color Meaning
TASK_ASSIGNMENT ListTodo green A task has been assigned to you
BLOCKER_ESCALATION AlertTriangle red A developer is blocked and escalated
REVIEW_REQUEST Check purple Your review is needed
DOCUMENTATION_REQUEST Info blue Documentation is needed
APPROVAL ShieldCheck emerald Board-level approval requested

Each notification optionally carries a related_task_id rendered as a deep-link to /tasks/{id}. For full details on types, icons, and adding new types, see docs/frontend/components/notification-types.md.

Dependency Management

Version Alignment

When bumping Next.js, always update eslint-config-next to match the same version. These packages must stay in sync. See UPGRADE.md for detailed procedures and troubleshooting.

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.