Skip to content

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.

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.

LayerTechnology
BackendNode 20+, Express 5, pino logging
FrontendReact 19 + Vite, @hello-pangea/dnd for drag-drop
Realtimesocket.io (server + client)
AuthJWT with bcrypt, persisted secret file
StorageMarkdown files on disk, gray-matter for YAML parsing
Dev toolingStorybook, 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
ServicePort
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.

The backend exposes a typical REST surface:

  • GET /api/tasks?project_id=atb — list tasks
  • POST /api/tasks — create
  • PUT /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.

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.

  • Markdown files, not a database. Every task is a standalone .md file. 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.json separates 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. todoin_progressreviewdone. Agents may only move to review; only humans promote to done. 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 right components/ / patterns/ / snippets/ folder, sidebar entry added to astro.config.mjs, npm run build clean, 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.

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 buildIndex function uses try/catch around per-file parse so one bad file doesn’t crash boot — but that meant a corrupted YAML file became invisible to GET /api/tasks/:id without warning. Added npm run audit:tasks so broken files fail loudly in CI instead of silently in prod. See patterns/silent-buildindex-failure.
  • Phased task templates. The phase-researchphase-planphase-implement chain exists because non-trivial work shouldn’t jump from description → code without an explicit research and plan step. Templates live at backend/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 in waiting_input until 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 .header in Starlight double-targets the sticky <header> AND an inner <div class="header">, producing stacked borders. Scope to header.header instead. Small, generalizable lesson. See snippets/starlight-header-selector-scoping.
  • No hot-reload on the backend. node server.js — no nodemon. Restart the task-board-be service after backend changes.
  • Tasks dir is gitignored (backend/tasks/**/*.md in .gitignore). That’s intentional — the tasks are personal/project-specific data, not source. Don’t commit them.
  • CLAUDE.md is 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.