Ludus
Ludus Magnus is the multi-agent software arena — it defines agents, their behavior, and how they are deployed. The name comes from the Ludus Magnus, the great training school adjacent to the Roman Colosseum. Multiple agent roles (Product Manager, Architect, Engineering Manager, Developers, DevOps, QA) collaborate across multiple software projects, coordinated by OpenClaw and communicating internally via the Beads protocol — a git-backed, DAG-based task and messaging system optimized for AI agents.
Key files: ludus/agents/*/ROLE.yaml, ludus/agents/agent-map.json, ludus/containers/Dockerfile.*
Commands: just deploy-agents, just deploy-full, just status, just images-build
Last verified: 2026-04-01
1. Vision — Two Tracking Systems
| System | Purpose | Lifecycle | Analogy |
|---|---|---|---|
| Beads | Internal agent coordination & communication | Ephemeral — clean up after completion | Slack / coffee machine chat |
| GitHub Issues | Official project tracking, external documentation | Permanent — part of the project record | Jira / ticket system |
No automatic sync between the two. Agents reference GitHub Issues in beads manually where needed (e.g., "see GH#42").
2. Roles
| Role | Repo Checkout? | Beads Usage | GitHub Usage |
|---|---|---|---|
| Product Manager | No | Creates epics, prioritizes, assigns | Creates issues |
| Architect | Sometimes (read-only) | Design decisions, reviews | Comments on PRs |
| Engineering Manager | No | Status overview, assignments, escalations | Reads issues |
| Developer | Yes (1-2 repos) | Tasks, results, questions | Creates/closes issues, PRs |
| DevOps | Yes (ludus) | Deployment tasks, incidents | Creates issues (ludus) |
| QA | Yes (read-only) | Bug reports, test results | Creates issues |
Roles like PM and Engineering Manager have no code repo checkout but need full Beads access. This drives the shared-intercom topology decision below.
3. Topology — Shared Intercom Clone
A local intercom directory lives on the host and is bind-mounted into every agent container at /mnt/intercom/. All agents share one Dolt database — changes are immediately visible, no import/export needed. The intercom does not use a git remote; it is initialised in-place via ludus ops intercom bootstrap (which runs bd init).
Host (ludus host):
/home/openclaw/b4arena/intercom/ ← local directory (no git remote)
└── .beads/ ← single shared Dolt database
Container (any agent, bind-mounted):
/mnt/intercom/.beads/ ← same database
Environment Variables
BEADS_DIR and BD_ACTOR are injected via docker.env, so bd works from any directory inside the container.
Constraint — Serialized Execution
Dolt is not safe for concurrent access. The watcher must serialize agent wake-ups — only one agent active at a time.
Benefits
- Immediate visibility — no sync delay between agents
- Simple agent code — agents just use
bd, no sync scripts to call - Container independence — containers are ephemeral, workspace data persists on host
- Zero scaling cost — new agents get access via bind mount
3b. Sandbox Container Architecture
All agents (including main) run their shell commands inside Docker containers managed by OpenClaw's sandbox system. The gateway runs on the host; only tool execution is containerized.
Ownership Split
| Concern | Owner | Managed via |
|---|---|---|
| Docker CE installed + running | ludus CLI | ludus ops provision |
| OpenClaw gateway (port, auth, channels, logging) | ludus CLI | ludus ops provision |
Agent defaults (agents.defaults.model) | ludus CLI | ludus ops provision |
| systemd, firewall, secrets (env file, PEM) | ludus CLI | ludus ops provision |
| All agent registration | b4arena/ | just register |
| All sandbox config (defaults + per-agent) | b4arena/ | just sandbox-configure |
| Sandbox Docker images | b4arena/ | just images-build |
| Agent workspace sync | b4arena/ | just sync (mutagen + rsync) |
Image Layers
All images are built on the ludus host via just images-build (no local Docker needed).
Per-Agent Sandbox Config
| Property | Default | main | forge |
|---|---|---|---|
| image | b4arena-base:latest | (default) | b4arena-dev:latest |
| memory | 2g | 2g | 2g |
| cpus | 1.0 | 2.0 | 4.0 |
| network | bridge | bridge | bridge |
| workspaceAccess | rw | rw | rw |
Global defaults are set via agents.defaults.sandbox.*, per-agent overrides via agents.list[].sandbox.*. Most agents (priya, atlas, rio, helm, indago, glue, etc.) use defaults.
All agents require network=bridge because they need outbound HTTPS access to GitHub (for code repo operations and gh CLI). The intercom git sync runs on the host between agent sessions, not inside containers.
Environment Variables
Env vars are injected into containers via agents.defaults.sandbox.docker.env (defaults) and per-agent agents.list[].sandbox.docker.env (overrides, merged on top):
| Variable | Source | Value |
|---|---|---|
BEADS_DIR | Default docker.env | /mnt/intercom/.beads |
BD_ACTOR | Per-agent docker.env | Agent-specific (e.g., main-agent, forge-agent) |
_TOKEN, _KEY, _SECRET patterns are stripped by the sanitizer — do not use these in env var names. GitHub auth is handled via bind-mounted credential files (not env var injection): the host env file is mounted at /etc/openclaw/env inside the container, where gh-wrapper.sh sources it to obtain GitHub App credentials for token generation.
Container Lifecycle
- Mode:
all— every session (including main) runs in a container - Scope:
agent— one container per agent, reused across sessions - Workspace access:
rw— containers bind-mount the host workspace directly (agent file changes persist on host) - Beads access: All agents share a single Dolt database at
/mnt/intercom/.beads(bind-mounted from host, bootstrapped once vialudus ops intercom bootstrap) - Creation: Containers are created eagerly at agent run start, before the first LLM token
- Reuse: Containers are reused if config hash matches; recreated if config changes
- Pruning: Auto-pruned after 24h idle or 7 days total age
Deploy Commands
just images-build # Build all 4 image layers on the ludus host
just sandbox-configure # Set sandbox defaults + per-agent overrides
just deploy # Full deploy: sync + register + sandbox-configure
just deploy-full # First-time: images-build + deploy
4. Four-Tier Execution Framework
Based on the Token-Saving Patterns research: Tokens are compute budget. Every token spent on a task that doesn't require reasoning is wasted.
| Tier | Description | Tokens | Beads Operations |
|---|---|---|---|
| 1 — System Cron | Deterministic scripts, zero reasoning | 0 | bd ready --json, bd mail --unread --json, notification formatting |
| 2 — LLM Cron | Tasks requiring interpretation | Yes (isolated) | Complex comment composition, status reports |
| 3 — Heartbeat | Multiple lightweight checks sharing context | Yes (shared) | Beads check as one heartbeat item |
| 4 — Pull Heartbeat | Agent self-serves from async work queues | Yes (reasoning) | Triage, prioritize, dispatch |
The Intern Test
"If given to an intern, would they need judgment or just follow a checklist?"
Checklist = Tier 1 (shell script). Judgment = Tier 4 (LLM agent).
Tier Classification for Common Operations
| Operation | Tier | Tokens? |
|---|---|---|
| Poll for new beads | 1 | No |
| Format notification summary | 1 | No |
| Claim a well-defined task | 1 | No |
| Route by label match | 1 | No |
| Triage: which beads are urgent? | 4 | Yes |
| Execute a task (code, analysis) | 4 | Yes |
| Write a comment on a bead | 2/4 | Yes |
| Create a new bead from findings | 2 | Yes |
5. Notification Flow
Architecture
┌─────────────────────────────────────────────────────────┐
│ Tier 1: Beads Watcher (system cron, every 2 min) │
│ │
│ Pure shell. Zero tokens. Zero LLM involvement. │
│ │
│ for each label in agent-map.json: │
│ bd ready --label <label> --json → collect bead IDs │
│ │
│ ├─ label "dev" → WAKE forge (with bead IDs) │
│ ├─ label "mgr" → WAKE rio (with IDs) │
│ ├─ label "main" → WAKE main (with bead IDs) │
│ └─ no label match → WAKE main (triage) │
└───────────────────────────────┬─────────────────────────┘
│ (only when work exists)
▼
┌─────────────────────────────────────────────────────────┐
│ Tier 4: Agent Wake-Up (tokens start here) │
│ │
│ Agent reads bead details via bd CLI │
│ → Claims task: bd update <id> --claim (atomic) │
│ → Executes work │
│ → Writes results: bd update, bd comment │
│ → Optionally creates/closes GitHub Issues │
└─────────────────────────────────────────────────────────┘
Wake-Up Mechanisms
| Method | Token Cost | Use Case |
|---|---|---|
openclaw agent --agent X --message "..." --json | Yes (full turn) | Primary. Triggers a full agent turn via gateway. |
openclaw agent --local --agent X --message "..." | Yes (full turn) | Embedded mode, no gateway needed. |
POST /hooks/wake | 0 (until agent wakes) | Remote triggers (CI/CD). |
openclaw system event was originally planned but does not exist as a CLI command. The dispatcher (beads-notify.sh) uses openclaw agent --message directly.
6. Agent Identity
Convention: agentId maps to BD_ACTOR + labels
Each OpenClaw agent uses two identity mechanisms in bd:
- Actor (
BD_ACTOR): Identifies who performs actions (audit trail, commit attribution) - Labels: Identifies what kind of work an agent handles (routing, filtering via
bd ready --label)
Agent "pm"
├── SOUL.md → personality & communication style
├── IDENTITY.md → "I am the Product Manager"
├── BD_ACTOR=pm → audit trail identity
└── labels: pm → watcher routes beads with label "pm" to this agent
Agent "dev-fe"
├── SOUL.md → personality & communication style
├── IDENTITY.md → "I am a Frontend Developer"
├── BD_ACTOR=dev-fe → audit trail identity
└── labels: dev, frontend → watcher routes matching beads here
Actor Resolution Order (bd CLI)
--actorflag (per-command override)BD_ACTORenvironment variableBEADS_ACTORenvironment variablegit config user.name$USERenvironment variable"unknown"fallback
Work Discovery
Agents find their work via label filtering, not actor matching:
bd ready --label dev --json # What's ready for dev role?
bd ready --label pm --json # What's ready for PM role?
bd ready --unassigned --json # Unclaimed work across all roles
7. Architecture Decisions
| # | Decision | Rationale |
|---|---|---|
| AD-1 | Watcher: System cron now, plugin later | Simplest tier. 15 lines of Bash suffices for prototype. Plugin only when dynamic agent list or lifecycle management is needed. |
| AD-2 | Identity: BD_ACTOR + label routing | Actor for audit trail, labels for work routing. Aligned with bd CLI identity resolution. |
| AD-3 | Human UX: Dashboard + Channel | Dashboard (bdui/beads-dashboard) for overview. OpenClaw channels (Telegram/Slack) for quick interaction. Both come "for free". |
| AD-4 | Topology: Shared intercom clone | Single Dolt database on host, bind-mounted into all containers. Agents use bd directly — no import/export. Watcher serializes execution and handles GitHub sync. |
| AD-5 | Interface: CLI bd via exec tool | Simplest start. All community patterns built on CLI. MCP server only if OpenClaw gains native MCP client support. |
| AD-6 | Dispatch: Tier 1 label routing | Zero tokens for routing. No agent ever polls. Watcher reads bd ready --json, groups by label, wakes the right agent. |
| AD-7 | Beads vs. GitHub: Separate worlds | Beads = internal Slack. GitHub Issues = official tickets. No sync. Manual references where needed. |
| AD-8 | Sandbox: all agents containerized, ludus-owned | All agents (including main) run in Docker sandbox containers. Custom images extend OpenClaw base with bd CLI. ludus ops provision handles Docker CE + gateway; Ludus owns all agent registration, sandbox config, and images. |
8. Implementation Phases
Phase 1: Shell Prototype (M1-M5)
Validate Beads workflows without OpenClaw dependency. Each milestone is a shell script that passes or fails.
| Milestone | Goal | Validation |
|---|---|---|
| M1 | Beads repo + two roles | bd ready --label dev --json shows correct beads |
| M2 | Watcher routes by label | Watcher outputs WAKE dev for labeled beads |
| M3 | Simulated agent loop | End-to-end: create, watch, claim, close |
| M4 | Agent-to-agent comms | Comments (questions/answers) + follow-up beads (delegation). bd mail skipped (requires Gas Town). |
| M5 | Multi-project labels | Routing by project + role label combination |
Phase 2: OpenClaw Integration (M6)
beads-notify.shdispatcher handles notify-and-pull wake-ups- WAKE lines mapped via label lookup in
agents/agent-map.jsontoopenclaw agentCLI calls - Agents use SOUL.md for personality +
bdCLI via exec tool for bead lifecycle - 19 acceptance tests with mocked openclaw CLI (no LLM tokens in CI)
Phase 3: Production Readiness
- Community dashboard deployment
- Langfuse/OTel integration for token tracking
- Natural language Beads interaction via OpenClaw channels
- Beads data retention policy (ephemeral cleanup)
References
- Beads Protocol — git-backed DAG issue tracker
- Token-Saving Patterns — Four-Tier Execution Framework
- Beads architecture research — Detailed Beads overview, community ecosystem, OpenClaw source references
- Multi-repo topology — Topology options exploration and decision record
- Sync patterns — Community sync patterns research
- External communication — OpenClaw external communication methods
- Sandbox container lifecycle — OpenClaw sandbox container internals (creation, reuse, pruning, mounts)