GroupChatPanel; its onboarding gate is TeamOnboardingGate; its orchestration engine is useBoardOrchestration. It is backed by /api/teams/:id/onboarding, /api/team-rules/:teamId, /api/chat-history, and /api/board.

Prerequisites
- A team with at least one agent. Create one from the Marketplace or the team sidebar, see Using teams.
- A connected runtime so agents can actually run. Group Chat’s send path is gated on a live OpenClaw Gateway connection (
canSendrequiresclient && connectionStatus === 'connected'). See Connecting runtimes.
The orchestration engine (
useBoardOrchestration) is always on; there is no toggle. It is gated only by the connection status and a history-hydration flag, so it stays quiet while the Gateway is down or while past messages are still loading.Steps
1. Open a team’s Group Chat
Select a team in the sidebar. When a team is selected and has agents, a Group Chat row appears at the top of the agent list (a team-photo button stacking the members’ avatars). Click it. Under the hood this callsopenGroupChat(teamId), which routes ContentArea to the { type: 'groupChat', teamId } view.
GroupChatView then swaps between three states, in order:
- A brief neutral placeholder while onboarding state hydrates from
GET /api/teams/:id/onboarding. - The full-window Know-Your-Team gate (no graph yet) when onboarding is incomplete.
- The settled team space split (graph on top, chat on bottom) once onboarding is complete.
2. Pass the “Know Your Team” gate (first open only)
The first time you open a team’s chat, the gate intercepts before the composer unlocks. It runs once per team and exists to prevent a message-flooding cascade (a naive first message once triggered ~40 responses). It has three phases (thePhase type: welcome → introducing → user-intro):
- Phase A: Meet your team. A welcome card shows the team’s agents and, when present, a “Led by Boo Zero” badge. Click Know Your Team (
data-testid="know-your-team-button") to begin. The button is disabled until a Gatewayclientis present and the team has agents. - Phase B: Agents introduce themselves. The gate sends a strict introduction prompt to each agent sequentially (one
chat.sendper agent over the team-scoped session). The prompt forbids@and teammate references; this is what kills the false-delegation cascade. The gate watches the chat store for a new assistant reply per agent and shows a “N of M done” counter. A refusal-shaped or too-short intro (the production smoking gun was a bareNO) triggers exactly one stronger retry before the gate accepts whatever lands and moves on. - Phase C: Your turn. A textarea (
data-testid="user-intro-textarea") asks you to introduce yourself. The submit button reads Continue to Team Space (data-testid="submit-user-intro"); your intro must be at least 5 characters. On submit, the text is persisted to SQLite viaPATCH /api/teams/:id/onboarding(theuserIntroTextfield, capped at 4000 chars server-side) and best-effort written to each agent’sSOUL.md. Boo Zero then acknowledges you in-character, and the split opens into the team space.
Your self-introduction (
userIntroText) is the source of truth in SQLite, not the SOUL.md write (Gateway SOUL.md persistence is unreliable). It is re-injected into the context preamble of every group-chat message, so agents always know who they are talking to, even on the very first message and after long gaps.3. Send a message
Type in the composer (placeholder “Message team… (@name to target)”) and press Enter. With no@mention, the message routes to the team’s leader. The routing priority in sendGroupChatMessage is:
- An explicit
@mention(see the next step). - Boo Zero, the universal, no-mention default.
- The team-internal lead (a CTO / Team Lead role detected at deploy time, stored as
team.leaderAgentId). - The first team member.
sendGroupChatMessage assembles a context preamble and prepends, in order: the Boo Zero rules block (only when the target is Boo Zero), the per-team brief (Boo Zero only, from /api/boo-zero/team-briefs/:teamId), the team rules block (/api/team-rules/:teamId), the merged recent-conversation preamble (including your userIntroText), and finally your message. The raw message, @mention included, is what shows in the transcript; the assembled context goes only to the Gateway.
4. Route with @mentions
Start a message with@AgentName to address one teammate directly instead of the leader. parseMention only matches an @ at the start of the message, using a longest-prefix, case-insensitive match against the team roster (team members and Boo Zero, so @Boo Zero works too). The matched name must be followed by whitespace or end-of-string. On a match, the @mention is stripped from the message the agent receives, and routing goes to that agent’s team-scoped session.
5. Watch delegations land on the board
When the leader (or any agent) emits a structured delegation, the orchestration engine turns it into a durable board task that appears inline in the chat timeline as aBoardTaskCard. This is the chat-fused board: the board is canonical, the chat is narration.
A delegation is a structured directive, not prose; the engine reads only typed signals: a sessions_send tool-call, or a <delegate to="@Name">task</delegate> (or multi-step <plan>) directive parsed once from the agent’s terminal turn. Per delegation, useBoardOrchestration (via the pure boardOrchestration core) does this:
- Derive: chat to board: the delegation creates a task, atomically claims it for the target, and opens an execution row.
- Round-trip: result to board: when the child finishes, its report-up summary and status are written to the board (never the raw transcript).
- Reflect: board to chat: completed tasks are batched (a 3-second window) into a single
[Task Update]delivered back to the leader for synthesis, plus a visible narration entry.
<plan> becomes a dependency chain where each step fires when its blocker completes. Delivery routes through a non-destructive nudge-queue, so a message to a busy teammate waits for its turn boundary instead of interrupting an in-flight run. The full model is in Delegation and orchestration and The board.
A risky-looking delegation (matching keywords like
delete, deploy, publish, rm -rf, secret, api_key) is surfaced on the leader’s approval queue via POST /api/governance/delegation-approval before it runs. Routine delegations never touch this path. The gate fails closed: an unreachable approval endpoint does not auto-approve.6. Stop a runaway team
While any agent is running or streaming, the composer’s Send button morphs into a red Stop button (data-testid="chat-stop-button"). Clicking it does two things at once:
- Bumps a monotonic
stopSignalcounter;useBoardOrchestrationwatches this and bails its in-flight delegation work at the next checkpoint (without tearing down the observers). - Calls
stopAllInTeam, which optimistically flips every running participant to idle locally, then fireschat.abortper running agent andsessions.abortper session as a backstop, and clears the in-memory wake/override state.
7. Capture a durable team rule with /rule
Type /rule <text> in the composer to add a durable rule for this team. handleSend intercepts it before routing to any agent: it appends the line to the team’s rules in SQLite (PUT /api/team-rules/:teamId), drops one confirmation meta entry into the merged transcript, and short-circuits; nothing is sent to the Gateway. The prefix must be /rule followed by a space and a non-empty body (so /rules or a bare /rule does not misfire).
Team rules are loaded and prepended to the context preamble on every future message (wrapped as [Team Rules — set by the user, authoritative]), so a correction like /rule don't do work yourself, delegate via <delegate> survives across sessions instead of rolling out of the recent-message window. The same rules text is also editable from the team’s Brief & Rules panel, see Boo Zero.
Options / variations
| Action | How | What it does |
|---|---|---|
| Address the leader | Send with no @ | Routes to Boo Zero (default), then team-internal lead, then first member |
| Address one teammate | @AgentName your message | Longest-prefix match at message start; @mention stripped before the agent sees it |
| Address Boo Zero explicitly | @Boo Zero … | Boo Zero is in the mention roster even though it is teamless |
| Add a durable rule | /rule <text> | Persists to /api/team-rules/:teamId; injected on every future message |
| Stop the team | Click the red Stop button | Bumps stopSignal + chat.abort/sessions.abort per running agent/session |
| Insert a mention | Click an agent chip | Inserts @AgentName into the composer |
Verify it worked
- After the gate, the composer unlocks and the placeholder reads “Message team… (@name to target)”. Re-fetch
GET /api/teams/:id/onboarding; bothagentsIntroducedanduserIntroducedshould betrue, anduserIntroTextshould hold your introduction. - Send a message that prompts a delegation; a
BoardTaskCardshould appear inline (assignee avatar, status badge, title) and progress fromin_progresstodone. Cross-checkGET /api/board?teamId=<id>; the same task is there. - A
/rule …message should drop a “Rule saved for team” confirmation and show up inGET /api/team-rules/:teamId.
Troubleshooting
Old chat history replaying as new delegations. This was a real production bug (a “7-hours-later cascade” of stale wake/relay messages). The fix is a history-hydration gate:
useBoardOrchestration is enabled only after every participant’s /api/chat-history fetch resolves (with a 5-second safety timeout), so hydrated entries are never treated as new work. If you see stale activity on team open, that gate is the thing to check.Related
- Peer chat, the mixed-runtime room model under the team chat
- Delegation and orchestration, structured tags, no-regex, the board-driven engine
- The board, the durable kanban the delegations land on
- Using teams · Boo Zero, team setup, leaders, briefs, and rules
- Connecting runtimes, bring a runtime online so agents can run