Atrium
Source: RogerSquare/atrium · Runs locally, not on a VPS Category: Project tour
Atrium — a kanban-style task management system built for autonomous AI developer agents. Humans get a visual control plane; agents pick up work through a REST API, update status as they go, and leave structured comments documenting their reasoning.
What it is
Section titled “What it is”A Node backend + React frontend that stores tasks as markdown files with YAML frontmatter on the local filesystem. Agents and humans interact through the same REST API; the task board UI renders the filesystem state in real time over socket.io.
The board is self-hosting — this wiki entry exists because a task in the board said to write it.
| Layer | Technology |
|---|---|
| Backend | Node 20+, Express 5, pino logging |
| Frontend | React 19 + Vite, @hello-pangea/dnd for drag-drop |
| Realtime | socket.io (server + client) |
| Auth | JWT with bcrypt, persisted secret file |
| Storage | Markdown files on disk, gray-matter for YAML parsing |
| Dev tooling | Storybook, Vitest, Playwright |
atrium/├── backend/│ ├── server.js # Express entrypoint│ ├── routes/ # tasks, projects, users, ai, design, services, ...│ ├── lib/ # constants, logger, projectRegistry, swagger, ...│ ├── tasks/ # THE data — see patterns/markdown-as-database│ │ ├── Atrium/<id>.md│ │ ├── Cairn/<id>.md│ │ ├── Artifex/<id>.md│ │ └── ...│ ├── projects.json # id → {name, folder} — see patterns/stable-project-ids│ ├── services.json # registered local services — see patterns/service-registry-control-plane│ └── users.json / chat/ / trash/├── frontend/│ └── src/│ ├── App.jsx│ ├── components/ # Board, TaskCard, TaskModal, Sidebar, Settings, ...│ ├── hooks/useTasks.js # central state + socket subscription│ └── contexts/AuthContext.jsx├── CLAUDE.md # protocol for AI agents — see patterns/agent-task-board-protocol├── README.md└── start.bat # starts both services in new windows| Service | Port |
|---|---|
| Backend (Express) | 3001 |
| Frontend (Vite dev) | 5173 |
| Storybook (if running) | 6006 |
Nothing on a VPS — Atrium is local-only. The tasks it tracks are about projects that may deploy elsewhere, but Atrium itself doesn’t.
How agents interact
Section titled “How agents interact”The backend exposes a typical REST surface:
GET /api/tasks?project_id=atb— list tasksPOST /api/tasks— createPUT /api/tasks/:id— update (status, assignee, content, etc.)
Agents follow the protocol in CLAUDE.md — use the API, don’t edit markdown directly, stop at review for human approval. See patterns/agent-task-board-protocol.
How services are controlled
Section titled “How services are controlled”Any local service registered in backend/services.json (id, port, cwd, startCmd) can be started, stopped, restarted, and tailed from the Atrium UI. The backend spawns child processes, tracks pids, and captures stdout/stderr. See patterns/service-registry-control-plane.
Non-obvious design choices
Section titled “Non-obvious design choices”- Markdown files, not a database. Every task is a standalone
.mdfile. Git can track them, humans can hand-edit them in an emergency, and agents write to them through the API so the activity log stays consistent. See patterns/markdown-as-database. - Project IDs stay stable across renames.
projects.jsonseparates id (atb,portfolio) from display name and folder. Renaming a project doesn’t break anything that references the id. See patterns/stable-project-ids. - Four statuses, no more.
todo→in_progress→review→done. Agents may only move toreview; only humans promote todone. Prevents “it shipped to itself” loops. - Wiki-update obligation. When a task ships a reusable component, a new pattern, or a non-obvious snippet, agents must document it in R-That Wiki (this site) before moving the task to
review— new page under the rightcomponents//patterns//snippets/folder, sidebar entry added toastro.config.mjs,npm run buildclean, deployed. See the “Wiki Documentation (STRICT)” block in CLAUDE.md for triggers and the full checklist. The wiki is the durable knowledge layer; task comments are transient.
Recent decisions
Section titled “Recent decisions”Atrium’s current design is the product of specific choices worth calling out. Each one is a real tradeoff, not a default.
- File-based storage over SQLite. Tasks are markdown files so humans can
grep, diff, and hand-edit them in an emergency; agents write through the API so the activity log stays consistent. Chosen after evaluating the alternatives. See patterns/markdown-as-database. - Surface silent index failures loudly. The
buildIndexfunction usestry/catcharound per-file parse so one bad file doesn’t crash boot — but that meant a corrupted YAML file became invisible toGET /api/tasks/:idwithout warning. Addednpm run audit:tasksso broken files fail loudly in CI instead of silently in prod. See patterns/silent-buildindex-failure. - Phased task templates. The
phase-research→phase-plan→phase-implementchain exists because non-trivial work shouldn’t jump from description → code without an explicit research and plan step. Templates live atbackend/templates/. See the “Phased Tasks (STRICT)” block in CLAUDE.md. - Agents must update the wiki on completion. Task comments are transient — they document what happened on one task, but future agents working on unrelated tasks won’t see them. The wiki is the durable layer. Codified in the “Wiki Documentation (STRICT)” block of CLAUDE.md.
- Mid-run approval checkpoints. When an agent hits genuine ambiguity, it emits
POST /api/approvals/task/<id>and the task pauses inwaiting_inputuntil a human answers. Better than guessing at a bad default. Codified in the “Mid-run Approval Checkpoints (STRICT)” block of CLAUDE.md. - MCP server for agent tool access, not prompt templates. Atrium’s REST surface is wrapped as an MCP server (
backend/mcp/), so Claude Code sees typed tools instead of stringly-typed curl commands. Stdio transport keeps the lifecycle bound to the editor session. See snippets/atrium-mcp-server-stdio-setup. - Scoped selectors when framework DOM has nested siblings. Overriding
.headerin Starlight double-targets the sticky<header>AND an inner<div class="header">, producing stacked borders. Scope toheader.headerinstead. Small, generalizable lesson. See snippets/starlight-header-selector-scoping.
Gotchas
Section titled “Gotchas”- No hot-reload on the backend.
node server.js— no nodemon. Restart thetask-board-beservice after backend changes. - Tasks dir is gitignored (
backend/tasks/**/*.mdin.gitignore). That’s intentional — the tasks are personal/project-specific data, not source. Don’t commit them. CLAUDE.mdis load-bearing. The file tells Claude how to behave inside the board. Edits to it change agent behavior globally. Treat it like a config file, not documentation.- Concurrent writes to the same task can clobber activity log entries. Unlikely with one human and one agent; becomes relevant if multiple agents run in parallel.