AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
A self-hosted 3D office where AI Agents work your GitHub issues.
Translation pending
Developer toolsTypeScript · HTMLGitHub @adrianeyreOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
A 3D office where AI agents work your GitHub issues, with a built-in load balancer for AI accounts
Taken from the repository README
Loading the full README.
Read the full README ↗A 3D office where AI agents work your GitHub issues. Each repository is a floor, each floor has a manager and a team of agents (coder, reviewer, QA, docs, designer), and you can click a monitor to see what an agent is doing, open the whiteboard to read the plan, or tell a manager "complete issues 1 to 5", or simply ask it for a change: it files the issues and adds contractors to the team for the work. A built-in load balancer spreads the agents' requests across your AI accounts (Claude, ChatGPT/Codex, Grok and more), with token caps per office, floor and agent.
Status: planning. Nothing is built yet. The requirements live in
docs/product-requirements-document/and the decisions indocs/architectural-decision-record/. This repository will become github.com/adrianeyre/ai-office.
The project website,
ai-office.adrianeyre.co.uk, shows
what AI Office does, with screenshots, a quick start for Windows, macOS
and Linux, and a full setup guide. Its source is site/:
pnpm site:build assembles it in site/dist/, copying in the
screenshots from docs/images/ and the logo and icons from apps/web,
and the Pages workflow (.github/workflows/pages.yml) builds and
publishes it on each push to main that changes it. The repository's
address on it is set once, in site/build.ts.
devEngines in
package.json; older versions are refused)pnpm install
| Command | What it does |
|---|---|
pnpm dev | Starts the development Postgres (Docker), applies the schema, builds any sandbox image not built yet, then runs the server (restarts on change) and the Vite dev server together; see Running in development |
pnpm lint | ESLint (type-aware) and a Prettier check |
pnpm typecheck | tsc for the root config and every workspace package |
pnpm test | Vitest, with each workspace package as its own project |
pnpm format | Prettier and eslint --fix, rewriting files in place |
pnpm build | Builds every package that has a build (the web app) |
pnpm db:schema | Apply the schema to the database at DATABASE_URL if it is empty, or check it is the same schema if not |
pnpm db:reset | Recreate the development Postgres, empty, and apply the schema |
pnpm db:up / pnpm db:down | Start / stop the development Postgres, and the sandboxes' network and sandbox relay (docker-compose.dev.yml) |
pnpm e2e | Builds the web app, then runs the Playwright end-to-end tests |
pnpm scenes | Screenshot tests of the 3D scenes in headless Chromium with software WebGL, without a GPU: the 3D Office's key scenes (see Scene screenshot tests) and a Floor in each 3D theme (see the guide) |
pnpm screenshots | Builds the web app, then retakes the README's screenshots in docs/images/ |
pnpm sandbox:build | Builds the sandbox images for Claude Code, Codex and opencode (needs Docker); for compose, |
AI Office has one address, http://localhost:3045 both in pnpm dev and
under compose: the web app, its /api, and the AI Gateway at /v1. Point
your own tools at it with a Gateway Key from API Key Management:
Claude Code with ANTHROPIC_BASE_URL=http://localhost:3045, Codex and
other OpenAI clients with http://localhost:3045/v1 (see
Gateway Keys). Whatever that
address is opened to, the Gateway is too, still behind Gateway Keys; to
open it beyond your machine, use compose's tls profile.
Tests that need Postgres start Postgres 18 with Testcontainers, so they need
Docker. Without it they are skipped with the reason (or fail when CI is
set, so CI never skips them). AI Office is not in production yet, so its
schema has no migrations: it is one plain SQL file, packages/db/schema.sql.
After changing packages/db/src/schema.ts (or
src/schema-extras.sql, the trigger and starting rows it cannot say),
pnpm --filter @ai-office/db db:generate writes it again, and
pnpm db:reset recreates the development database from it.
pnpm e2e builds the web app and runs the Playwright suites in e2e/ in
Chromium. Postgres 18 is started once with Testcontainers and the schema is
applied to a template database. Each test then gets a fresh copy, and its own server
process on a free port, so tests start from a first run and can run in
parallel. Each server's GitHub App talks to the test's own fake GitHub
(office.github), served over HTTP through GITHUB_API_URL.
The server process is e2e/server/main.ts: AI Office started as
apps/server/src/main.ts starts it, plus an IPC channel to the test. Through
it a test does what no part of AI Office does yet: office.raiseNotice()
raises a Notice, and office.publish() publishes an event on the bus.
office.pollIntake() runs issue intake at once. With no Docker, Agents'
tasks go to a stand-in sandbox runner (startOffice's sandboxRunner
option), which records each task's Agent, AI Provider, Model and effort
(office.sandboxTasks()) and fails it.
pnpm --filter @ai-office/e2e exec playwright install chromium # once
pnpm e2e
Without Docker the tests are skipped with the reason (CI fails instead).
To use a Postgres you already run, set E2E_POSTGRES_URL to a superuser
URL for it. Failed tests keep a trace, a screenshot and the server's log in
e2e/test-results/ (pnpm --filter @ai-office/e2e exec playwright show-trace <trace.zip>); CI uploads them as the playwright-traces
artifact.
pnpm scenes compares the 3D Office's key scenes with reference
pictures (PRD §9, Visual): the lobby, its reception desk close in, a
Floor, the same Floor with the Context Usage bars and tokens above its
Agents' heads, its meeting room and the Avatar Editor's preview. It needs
no GPU, no Docker and no server.
OfficeScene, AvatarPreview) on a page of their own,
apps/web/screenshots/, never part of the app's build. Its data is
fixed (screenshots/scene-data.ts). It has a Floor of seven Agents
whose Avatars come from fixed seeds (seededRandom), and they show
every state the Office draws at a desk: working at a lit monitor,
waiting on review, paused, capped, and piles of tickets. It also has
fixed Notices on the boards and lift stops, and in the lobby the
Communications Agent at reception (its Avatar from a fixed seed) and
the visitor, three envelopes in the out-tray and a failing Connection's
red marker (FR-96). Above the Floor's Agents' heads (FR-62, FR-85), the
busy coder has its Context Usage bar and its tokens today, the idle
designer its tokens alone, and others tokens at 80% and at 100% of the
daily cap, built from fixed Context Usage and token usage as the API
sends them. Each scene has a fixed camera: the Office's
overview for the lobby and both Floor scenes, the view the Office flies to when
the Communications Agent is clicked for the reception desk (without the
marker it differs by 741 pixels; the overview alone would not notice),
a view into the meeting room, and the preview's own. They are drawn at Medium
quality with reduced motion, so nothing moves. The models and the logo
load before the first frame. After ten frames the page stops drawing
and says it is ready (data-scene="ready" on <html>).--use-angle=swiftshader), at
1280 × 720 and a device pixel ratio of 1. Each test checks it really
draws with SwiftShader. Only the canvas is compared, so fonts play no
part, except in the scene with the displays above the Agents' heads:
they are HTML over the canvas, so that one compares the canvas and its
labels together, after checking the labels' text and colours in the
page. The page sets its fonts to DejaVu (screenshots/styles.css),
which Debian and Ubuntu both ship, so the labels' text is drawn alike
on every Linux. Without the coder's bar it differs by 1,653 pixels.e2e/scenes/scenes.spec.ts): a pixel counts as
changed past Playwright's default colour threshold (0.2), and a picture
fails when more than 100 pixels change. Repeated runs of the same build
differ by none. One Agent stopping work on the Floor changes 252 pixels,
and another hair colour in the preview changes 418.e2e/scenes/reference/, named
for Linux, where CI runs them. After a change to how a scene looks, look
at the new pictures, then take them again and commit them:pnpm --filter @ai-office/e2e exec playwright install chromium # once
pnpm scenes # compare
pnpm scenes --update-snapshots # take new reference pictures
e2e/playwright.scenes.config.ts builds the page and serves it on
127.0.0.1:3049 (pnpm --filter @ai-office/web screenshots:serve), as its
key-scenes project. The same command also runs the Floor themes'
screenshot tests, its chromium project, on a page of their own (see
the guide);
--project=key-scenes runs these alone. A failed
comparison leaves the actual, expected and diff pictures in
e2e/test-results/scenes/. CI's Screenshots job uploads them as
the screenshot-differences artifact.
The workspace follows the layout in §8 of the PRD: apps/server,
apps/web, and
packages/{orchestrator,gateway,github,communications,db,events,shared}.
Every Agent but the Manager fills a Role from the office Role library
(the roles table): a name, instructions, Skills, desk dressing, a
suggested AI Provider and Model, and one of four fixed Stages: work,
review, QA and docs
(ADR 0003).
The library starts with coder, reviewer, QA, docs and designer. Each
Stage has its label, and an issue moves through them:
needs-work → needs-review → needs-qa → needs-docs → ready-to-merge (or needs-human)
The pipeline, the Review Loop, Round Caps, the Merge Policy and dispatch
go by a Role's Stage, never its name, so a new work-Stage Role is
dispatched like a coder. When a Floor has several work Roles (a
merchandiser and a copywriter, say), the Manager's triage gives each
issue to one by the Roles' routing descriptions; a role:<name> label,
which AI Office creates for each work Role, forces one, and when the
Manager cannot tell it asks the Owner with a Notice offering each
candidate Role
(routing).
A Batch's Plan may instead give an issue an ordered list of work Roles
(the merchandiser, then the copywriter), which take turns on its one
branch, each handing it to the next, before review; the Owner can
reorder or remove them on the whiteboard, and a Review Loop fix goes
back to the Role whose changes it concerns
(several work Roles on one issue).
In Admin → Role library (/general-settings/roles) the
Owner makes, duplicates and edits Roles, and deletes one no Agent fills;
Floor Settings adds Agents in any of them. Changing a Role changes every
Agent with it from its next task (the editor says how many), and an
Agent's own extra instructions still apply on top. Every change is in
the Activity Feed. needs-work
was called needs-code: on a Floor added before, AI Office renames the
repository's label when it starts, and GitHub keeps it on its issues. See
Floors and Agents.
Adding a Floor means picking a Floor Template: its starting team,
settings and labels. Admin → Add a Floor shows each template's name,
description, icon and starting team, and starts on Development, the
team every Floor had before templates (a Manager, a coder, a reviewer and
QA). Blank is a Manager only. Online store (a merchandiser, a
copywriter, a storefront developer, a brand reviewer and store QA),
Website & marketing (a web developer, a copywriter, an SEO specialist
and a conversion reviewer) and Content & blog (a writer, an SEO
specialist, an editor and a fact-checker) set up Floors that are not for
software: each runs its checks as npm scripts that its suggested first
issue sets up, and works on its repository only, never on a live store,
an ad platform or customer email. Documentation (a technical writer,
a docs reviewer and an example checker), Design system (a designer, a
front-end developer, an accessibility reviewer and visual QA), Data &
analytics (an analytics engineer, a data reviewer and data-quality QA),
Infrastructure (a platform engineer, a security reviewer and plan
QA) and Mobile app (a mobile developer, a reviewer and device-test
QA) do the same for their own checks: the docs build, links and runnable
examples; component tests, visual snapshots and contrast; dbt test and
SQL lint on sample data; terraform plan from no state and policy
checks, and never an apply; unit and simulator tests. Each says plainly
what its Floor never acts on (the live docs site, a package registry,
the production warehouse, live infrastructure, the app stores). The
template's Roles and Skills join
the Role and Skill libraries where they are missing; the Floor remembers
which template it started from, and nothing depends on the template
afterwards. Each template has a 3D theme, which the Floor keeps: stock
shelves and parcels for Online store, a photo studio for Website &
marketing, bookshelves and a printing press for Content & blog, a wall of
bookcases for Documentation, colour swatches for Design system, wall
dashboards for Data & analytics, server racks for Infrastructure and a
wall of phones for Mobile app; Development and Blank keep the office as
it was. Each desk is dressed as its Role is in the Role library (a
copywriter's notebooks, a merchandiser's product samples). Themes and
dressing are decoration only. Built-in
templates are folders under templates/ (template.yaml,
roles/<role>.md and skills/<name>/), loaded and checked as AI Office
starts.
In Admin → Floor Templates (/general-settings/templates) the Owner makes
templates of their own: they duplicate one (the built-in ones are
read-only) and edit its Roles and counts, settings, the Manager's
guidance, labels and theme; save a Floor's set-up as a new template from
its admin page; export a template, with its Roles and Skills, to a file;
and import one from such a file or a Git repository. An imported template
shows its full contents (every Role's instructions and every Skill) and
its clashes with the Role and Skill libraries, and no Floor can be added
from it until the Owner has read it and enabled it. The Owner's templates
are stored in AI Office, every add, change and removal is in the Activity
Feed, and they are part of configuration export and import. See
Floors and Agents.
Taken with pnpm screenshots, which runs the built app against Postgres with a
fake GitHub and writes them to docs/images/.
How AI Office works, part by part, is in docs/guide/:
| Guide | What it covers |
|---|---|
| Running AI Office | Running AI Office in development, with docker compose, the server's first run and sign-in, freeing storage, retention, and configuration export and import. |
| The AI Gateway | The built-in AI Gateway: its endpoints, prices, analytics, request history, Gateway Keys, AI Accounts, the outbound proxy, the Decision Model and AI Providers. |
| Floors and Agents | Floors and Floor Templates (the built-in ones and the Owner's own), Agents and their editors, the Role library, Token Caps and the Git identity. |
| The issue pipeline | From issue to merged pull request: intake, triage, sandboxes, reviews, QA, the designer, docs, the Merge Policy and the Manager's watchdog. |
| GitHub, events and the Manager chat | The GitHub App client, the event bus, the Activity Feed, the Agent live view, the Manager chat (clearing and compacting it) and its Grilling Sessions, and the direct chats with each Agent and the Terminal. |
| Batches, Plans and memory | Batches and Plans on the whiteboard, the Kickoff and the Wrap-up, and repository, office-wide and Role memory. |
| Notices, the wall board and Skills | The Notice Board, browser notifications, the Communications Agent and its Connections (with Wrap-up reports to Confluence, Notion, Google Docs, Slides and Drive, a work log in Google Sheets and an event per Batch in Google Calendar, through Google sign-in for Google), the wall board and the Skill library. |
AI Office's own issues are worked by a coding agent inside a Docker sandbox
using Sandcastle
(@ai-hero/sandcastle). Setup, running, writing prompts and landing a run
are covered in
docs/processes/running-sandcastle.md.
cp .sandcastle/.env.example .sandcastle/.env # then fill in credentials
pnpm exec sandcastle docker build-image
git checkout -b issue-9-some-slice origin/main
pnpm sandcastle 9
| Path | What it is |
|---|---|
GLOSSARY.md | The project's vocabulary; use its terms exactly |
docs/product-requirements-document/ | What AI Office must do |
docs/architectural-decision-record/ | Decisions that must not be quietly reversed |
docs/processes/ | How work gets done: setting up the GitHub App and your Floors (everything GitHub needs, from the test repository and the App to branch protection, labels and intake), deploying to EC2 (HTTPS with Caddy, access, backups and upgrades) and running Sandcastle |
docs/guide/ | How AI Office works, part by part |
docs/pen-testing/ | The pen-testing round: the pen-test stack, its triage runbook and assessments |
docs/images/ | The README's screenshots (pnpm screenshots) |
docs/reference/ | Reference specs, such as the art direction |
templates/ | The built-in Floor Templates, their Roles and Skills |
AGENT_UID=1000 AGENT_GID=1000 pnpm sandbox:buildpnpm sandbox:ensure | Builds only the sandbox images not built yet; pnpm dev runs it first |
docker compose up --build | Runs the whole AI Office in Docker: the app, Postgres, and Agents' and the Manager chat's sandboxes, started from inside the app (build the sandbox images first, as above); see Running with docker compose |
| The web app and the 3D Office | The company logo, link previews, accessibility, running the web app, the 3D Office and phones. |