108 lines
5.7 KiB
Markdown
108 lines
5.7 KiB
Markdown
# Architecture
|
|
|
|
## Service Stack
|
|
|
|
Single unified service (`ejclaw`) manages all three Discord bots in one process:
|
|
|
|
- `ejclaw.service` — Single unified process
|
|
- Discord bots: `DISCORD_OWNER_BOT_TOKEN` (owner), `DISCORD_REVIEWER_BOT_TOKEN` (reviewer), `DISCORD_ARBITER_BOT_TOKEN` (arbiter)
|
|
- Paired review: owner ↔ reviewer (agent types configurable per role)
|
|
- Reviewer fallback: Claude exhaustion → codex-review auto-handoff
|
|
- Shared dirs: `store/`, `groups/`, `data/`
|
|
- SQLite WAL mode + `busy_timeout=5000` for concurrent access
|
|
|
|
## Data Flow
|
|
|
|
```
|
|
Discord ──► SQLite (WAL) ──► GroupQueue ──┬──► Owner (host process)
|
|
│ │
|
|
│ ▼ (auto-trigger)
|
|
├──► Reviewer (host process, read-only guarded)
|
|
│ │
|
|
│ Verdict routing
|
|
│ ├─ DONE → change detection → finalize or re-review
|
|
│ ├─ BLOCKED → Arbiter (if enabled) or User
|
|
│ └─ Feedback → Owner (loop)
|
|
│
|
|
├──► Arbiter (on-demand, fresh session each time)
|
|
│ │
|
|
│ ┌───┴─── MoA (if enabled) ───┐
|
|
│ │ Ref model A ──► opinion │
|
|
│ │ Ref model B ──► opinion │
|
|
│ │ → injected into prompt │
|
|
│ └────────────────────────────┘
|
|
│ │
|
|
│ PROCEED/REVISE/RESET/ESCALATE
|
|
│
|
|
IPC polling ◄── follow-up messages
|
|
│
|
|
┌────────── Router ──────────┐
|
|
▼ ▼
|
|
paired_turn_outputs Discord (display only)
|
|
(agent ↔ agent data) (user observation, @mention)
|
|
```
|
|
|
|
## Room Assignment Model
|
|
|
|
Per-room routing uses an explicit assignment model:
|
|
|
|
- `room_settings` is the room-level source of truth (SSOT)
|
|
- Each room stores:
|
|
- `room_mode`: `single` or `tribunal`
|
|
- `owner_agent_type`: `codex` or `claude-code`
|
|
- Public room assignment uses `assign_room`
|
|
- `registered_groups` is a legacy projection being removed from setup/verify/runtime read paths
|
|
- Refactor target and migration sequence live in `docs/legacy-compat-removal-spec.md`
|
|
|
|
Operationally:
|
|
|
|
- `single` → one owner bot
|
|
- `tribunal` → per-room owner + globally configured reviewer + optional arbiter
|
|
|
|
Tribunal is no longer inferred from "two bots registered on one room"; it is an explicit room setting.
|
|
|
|
## Tribunal 3-Agent System
|
|
|
|
```
|
|
User message
|
|
→ Owner responds (implementation, answer, etc.)
|
|
→ Reviewer auto-triggered (critical review, design check)
|
|
→ Verdict:
|
|
DONE → Owner finalizes → Task completed → @user ✅
|
|
DONE_WITH_CONCERNS → Owner addresses feedback → loop
|
|
BLOCKED/NEEDS_CONTEXT
|
|
├─ Arbiter enabled → Arbiter judges → PROCEED/REVISE/RESET/ESCALATE
|
|
└─ Arbiter disabled → Escalate to user → @user ⚠️
|
|
→ Owner BLOCKED/NEEDS_CONTEXT → Arbiter (same path as reviewer)
|
|
→ Deadlock (2+ round trips without progress)
|
|
→ Arbiter summoned → binding verdict → loop resumes
|
|
```
|
|
|
|
## Verification Execution
|
|
|
|
Reviewer and arbiter now run as host processes with role-scoped read-only
|
|
guards and sandbox settings. Verification profiles run directly on the host with
|
|
restricted environment variables and snapshot checks:
|
|
|
|
- Fixed-profile direct execution (`test`, `typecheck`, `build`)
|
|
- Snapshot comparison before/after execution
|
|
- Root-level runtime/build directories excluded from verification snapshot
|
|
- Role-scoped reviewer safeguards remain independent of verification execution
|
|
|
|
## Key Files
|
|
|
|
| File | Purpose |
|
|
| ------------------------- | ---------------------------------------------------------- |
|
|
| `src/index.ts` | Orchestrator: state, message loop, agent invocation |
|
|
| `src/agent-runner.ts` | Spawns agent processes, manages env/sessions/skills |
|
|
| `src/verification.ts` | Fixed-profile verification execution with snapshot checks |
|
|
| `src/channels/discord.ts` | Discord channel (8s typing refresh, Whisper transcription) |
|
|
| `src/ipc.ts` | IPC watcher and task processing |
|
|
| `src/router.ts` | Message formatting and outbound routing |
|
|
| `src/config.ts` | Trigger pattern, paths, intervals |
|
|
| `src/task-scheduler.ts` | Runs scheduled tasks |
|
|
| `src/db.ts` | SQLite operations |
|
|
| `runners/agent-runner/` | Claude Code runner (Agent SDK) |
|
|
| `runners/codex-runner/` | Codex runner (SDK, `codex exec` wrapper) |
|
|
| `groups/{name}/CLAUDE.md` | Per-group memory (isolated) |
|