Skip to main content
A team is the unit Clawboo coordinates around: a durable row that groups agents deployed together and names an optional leader. Inside a team, agents may run on different runtimes, one OpenClaw agent, one Clawboo Native agent, one Hermes agent, and still collaborate as peers. They can do that because Clawboo splits the world into two planes. The shared plane is everything the whole team coordinates over: the board, team chat, shared memory, the capability inventory, and Routines. It is canonical and cross-runtime; every runtime reads and writes the same shared plane through the MCP spine. The private plane is everything a runtime keeps to itself: its native memory, its self-created skills, its session/transcript state, and its own delivery channels. Clawboo observes the private plane (it materializes a stable home for it, attaches MCP to it) but never clobbers it. This page explains what a team is, what lives on each plane, how the boundary is derived purely from a runtime’s declared capabilities (never a runtime-id switch), and why “preserve, don’t homogenize” is the rule that lets a mixed-runtime team work.

What it is, and what it isn’t

A team groups agents and a leader; it is not a workspace, a tenant, or an execution context. It owns identity (name, icon, color), an optional colorCollectionId and templateId, an optional leaderAgentId, and an isArchived flag. It does not own agents; agents reference their team through a nullable teamId foreign key, so an agent belongs to at most one team (or none), and “the agents on team X” is a query, not a stored list. A team’s agentCount is computed on read (SELECT COUNT(*) FROM agents WHERE team_id = ?). The shared/private split is not a privilege boundary and not a sandbox. It is a coordination boundary. The shared plane is the single source of truth the whole team agrees on; the private plane is where a runtime’s own cognition compounds. The split exists so that a team can mix runtimes without forcing every runtime into a lowest-common-denominator behavior; a runtime that has rich native memory or self-improving skills keeps them, while still pulling shared facts and pushing shared decisions through the same MCP servers as every other runtime.
Teams are single implicit tenant today. The teams row (and every other table) carries a dormant tenant_id column, but no per-tenant filtering is active. Multi-tenant scoping is a future seam, not a shipped feature.

The model

A team is N agents, each on some runtime. They all share one shared plane; each keeps its own private plane. The solid arrows are the shared plane; every agent reads and writes it the same way. The dashed arrows are private planes; each is owned by its runtime; Clawboo observes them but routes nothing between them.

How it works

Teams are a thin grouping row

The teams table holds identity and a leader pointer; agents carry the membership. This keeps a team a label over the agent registry rather than a container that owns runtime state. Re-coloring a team, renaming it, or changing its leader touches only the teams row; moving an agent between teams is a write to that agent’s teamId. Because membership lives on the agent, the agent registry of record stays the single owner of who exists, and the team is just how Clawboo scopes coordination.

The shared plane is the same for every runtime

Every shared-plane subsystem is keyed by team, and every runtime reaches it through the same surface:
  • The board is canonical for task/coordination state. A delegation becomes a board task; a claim is atomic and single-assignee; an outcome lands on the board. Chat is narration of the board, never a write path back to it. See The board.
  • The team chat room is team:<teamId>, one room per team by default. A post is delivered to every teammate except its own author (the per-(roomId, authorAgentId) echo guard is just the read’s excludeAuthorId). A teammate’s post arrives tagged as evidence, never as a user instruction. See Peer chat.
  • Shared memory is team-shared by construction. When a run auto-saves a fact, it is tagged with the team only (agentId left null), so any teammate on any runtime recalls it; reads see team-shared plus global plus this-agent-private, never another team’s. See Memory.
  • The capability inventory and Routines are likewise team-scoped. Routines are the single external wake for every runtime class; a scheduled fire is just another board-task dispatch through the standard executor pipeline (budgets, verification, observability all apply), never a side channel into a runtime’s own cron.
The point is uniformity: a Hermes agent and a Clawboo Native agent on the same team see the same board, the same chat, and the same team facts, because all three arrive over MCP / REST keyed by the same teamId.

The private plane is derived from capabilities, not runtime ids

Which native powers a runtime keeps is declared by the RuntimeAdapter, not hardcoded by Clawboo. A runtime’s capabilities() carries a native-preservation seam: runtimeClass, a nativeHome claim (scope + persist), and nativeSkills / nativeMemory / nativeChannels / nativeScheduler flags. The host turns those claims into a normalized integration plan with one pure function, and branches only on that plan, never on a runtime id:
resolveRuntimeIntegration(caps): {
  home: { kind: 'persistent'; scope: 'per-identity' } | { kind: 'ephemeral' } | { kind: 'connected' }
  preserveSkills: boolean
  preserveMemory: boolean
  useGatewayChannels: boolean
  coRunScheduler: false   // always false — the host's scheduler owns when-to-run
}
The function is conservative by default: an adapter that omits the seam resolves to runtimeClass: 'wrapped-oneshot' with an ephemeral home and nothing preserved; so omission never silently preserves or strips state beyond the plain one-shot path. A persistent home is granted only when the claim is scope: 'per-identity' and persist: true; the preserve flags are clamped to a persistent home, so a misdeclared adapter degrades to the safe default rather than producing a contradictory plan (preserving state inside a throwaway home is incoherent). For a connected-substrate runtime the host manages no filesystem home at all; the substrate owns its world entirely and the host drives it over its live connection. Here is how each of the five runtimes declares its private plane, and what the host does with it:
RuntimeruntimeClassnativeHomenativeMemorynativeSkillsnativeChannelsWhat the host manages
openclawconnected-substrate(omitted)preservepreservegatewayNothing; the Gateway owns its state dir; runs ride its live connection
clawboo-nativenativeper-identity, persistpreservenonenoneA persistent per-identity home holding persisted transcripts
hermeswrapped-oneshotper-identity, persistpreservepreservenoneA persistent per-identity home where MEMORY.md, state.db, and skills/ compound
claude-codewrapped-oneshot(omitted)nonenonenoneNothing; the SDK runs against the user’s real home
codexwrapped-oneshotper-run, persist:falsenonenonenoneAn ephemeral per-run home (auth lives in CODEX_HOME)
When the plan says home.kind === 'persistent', the host materializes one stable home per (runtime, agentId) under Clawboo’s own state dir, owner-only (0700), before the driver touches it; that home is where the runtime’s private memory and skills accrue across runs. The runner only computes the path; the driver provisions inside it.

Observe, but never clobber

“Never clobber” is enforced at every write boundary:
  • Hermes home. Clawboo writes exactly two things into a Hermes home: mcp.json (Clawboo-owned, refreshed every run because the server port can change) and a one-time config.yaml seeded copy-if-absent from the user’s real ~/.hermes. It never overwrites the seeded config and never writes back. Everything else in that home, MEMORY.md, state.db, self-created skills, is Hermes’s, and it persists across runs.
  • The verification critic. The builder-≠-judge reviewer is built without a homeDir on purpose: the critic must not share the builder’s native memory. Preservation is per-identity precisely so a fresh, independent reviewer can run on the same runtime without inheriting the builder’s private state. See Verification.
  • The AgentSource sync. When the Gateway syncs agents into SQLite, the upsert touches only Gateway-owned columns; teamId, personalityConfig, execConfig, avatarSeed, runtime, and capabilities are Clawboo-native and preserved across every re-sync. A runtime’s contribution to the shared registry never overwrites the team’s local decisions about that agent.

Design rationale and trade-offs

The split exists because the alternative, a single homogenized agent model, throws away exactly what makes a multi-runtime team worth assembling. A runtime that has built a rich MEMORY.md or a library of self-created skills is more useful with that state intact; flattening every runtime to a stateless worker would erase its accumulated competence. So Clawboo preserves the private plane and unifies only the coordination plane. Deriving the boundary from capabilities rather than runtime ids is what keeps that promise honest. Because the host branches on resolveRuntimeIntegration(caps).home.kind and never on runtime === 'hermes', a new runtime declares its own integration depth and slots in unchanged, and a misdeclared adapter degrades to the conservative one-shot path instead of silently corrupting state. The trade-off is a second persistence surface per preserved runtime (a per-identity home beside the shared board and memory), and a strict discipline at every write boundary so a shared-plane write never reaches into a private plane. The shared plane is canonical for a related reason: with N runtimes contributing to one team, the team needs exactly one place that everyone agrees on. Making the board, chat, memory, and Routines the canonical surfaces, and treating each runtime’s own kanban, channels, or cron as private, avoids two sources of truth, the drift and double-dispatch races that come with them.

Boundaries and non-goals

  • A team is not a tenant. Every table carries a dormant tenant_id, but no per-tenant scoping is active in v0.2.0. Multi-tenant teams are a future seam, not a shipped feature.
  • An agent belongs to at most one team. Membership is a single nullable teamId foreign key; there is no many-to-many agent↔team join. A documented future seam (an agent_team_memberships table) would change this; it is not built.
  • Clawboo does not bridge private planes. It never syncs a runtime’s native kanban, never serves a runtime’s messaging channel, and never co-runs a runtime’s own scheduler for teammate dispatch (coRunScheduler is hard-typed false). Cross-runtime collaboration happens only on the shared plane.
  • The split is coordination, not isolation. It does not enforce a security or privilege boundary. Concurrency isolation for file-mutating work is the job of per-task worktrees, not of the plane split.
This documents the v0.2.0 working tree (commit 03b206a). The current npm latest is clawboo@0.1.9, so npx clawboo installs 0.1.9 until the v0.2.0 tag is published. Differences are noted in Known Issues.

See also

  • The agent model: Boo, Boo Zero, and the five runtime classes
  • The board: the shared plane’s canonical coordination substrate
  • Peer chat: the team chat room and how peer posts arrive
  • Memory: the shared memory tier vs. each runtime’s private tier
  • Scheduling: Routines as the single external wake
  • Verification: why the critic runs without the builder’s home
  • Connecting runtimes: bringing a runtime online for a team
  • Glossary: canonical term definitions