Rome
A self-hosted agent runtime that treats the environment around a model as the thing worth growing: a Rome App packages an interface, executable actions, on-demand skills and a private database as git-tracked code an agent can reuse later, while the runtime gives every delegated subagent a child session of its own, forks a turn without letting it mutate its source, and fails a turn rather than quietly swapping the model that produced the conversation.

What it is
A self-hosted agent runtime whose product is the environment around the model, not the model itself. It is an MIT-licensed pnpm monorepo: a quickstart script pulls a Docker image and serves a guardian-only dashboard on http://localhost:7663. The unit is a Rome App — an app.yaml manifest plus typed actions, app-owned agents, on-demand skills, lifecycle hooks, a private database and an optional web interface — and the thirteen first-party apps load through the same installer a community app would. Two harnesses sit behind the agent layer — Anthropic through the Claude Agent SDK, OpenAI through Codex — chosen per agent by tier or exact model id. Underneath is a session runtime: durable sessions and transcripts, a child session of its own for every delegated subagent, forked turns in an isolated or an exact mode, an input queue that steers a running turn instead of replacing it, and a model pin that fails closed. Memory is files synced through git, and the compounding it argues for is executable — actions, skills and apps as code later work discovers and reuses.
Who built itAn organization account rather than a person: the repository is rome-os/rome and its licence is MIT, while one contributor carries the history. The account zhangfand wrote 193 of the 352 commits, committing as Zhangfan under dong.zhangfan@gmail.com, and the next four accounts — yunfanye, Jessie-QingYu, zoolsher and Asuka109 — account for 129 more between them. Eighteen accounts are on the contributor list, including a release bot with thirteen commits, and 215 commits carry a co-author trailer that names a person, the bot, or a Claude model.
How it is put together
The parts · 6A single composition root over a flat module system, with the environment as the product rather than the model. The monorepo splits into a backend runtime, a guardian-only dashboard, an Electron shell, a phone host, two public app SDKs and thirteen first-party apps that are installed through the same path as anyone else’s, which is what makes the app surface a plugin system instead of a feature list. Three ideas hold the runtime together. A session is the durable boundary and a turn is the unit that is counted and charged, so everything that allocates work — a delegated subagent, a forked branch, an inbound channel message, a scheduled routine — becomes a session with its own evidence. Process isolation is granted in one place only, which turns nesting into data the runtime carries rather than operating-system parentage it inherits. And the environment is code, so memory, skills, actions and apps all land in files inside a git repository the user owns, which is also the answer to what all this compounding is made of. Documentation is part of the design rather than an artefact of it: the rulebooks under docs/authoring/ carry tier tags and an eviction rule, the architecture family may not cite file paths, and the prose is linted in continuous integration.
- packages/core/
- The backend runtime: 1,005 files and 10,885 KB. The core agent layer holds an agent session of 143,888 bytes and an agent runner of 27,878, a 47,370-byte Anthropic provider and a 52,602-byte Codex app-server provider, a 51,989-byte MCP façade, subagent execution, hook recursion, the input queue and the turn-stream registries. Beside it sit a 61,425-byte action engine with worker RPC and IPC, an app manager and installer with packaging, publishing, remix and a Store client, 26 database repositories led by an 84,856-byte webchat repository, a routine engine with schedule and event triggers, a git-backed sync layer, channels for a dozen platforms, and a 68,699-byte index.ts that wires the object graph.
- packages/web/ and packages/ui/
- The guardian-only dashboard, 775 files and 7,964 KB, built as an SPA with page-level files such as an 89,871-byte routines page and a 79,635-byte chat component, a storybook, i18n, mock fixtures including recorded app bundles, and end-to-end specs for layout invariants, touch targets and control-size vocabulary. The 106-file component kit beside it is the shared surface for the dashboard and for app interfaces.
- packages/desktop/ and packages/mobile/
- An Electron shell and local runtime, 76 files and 9,870 KB, which vendors a Windows toolchain under desktop/vendor, plus a phone host of 60 files that runs the dashboard inside a WebView and supplies the platform navigation the WebView lacks. Each has its own build and publish workflow.
- rome_apps/
- Thirteen first-party apps, each a workspace package with an app.yaml, actions, agents, skills, database migrations, an optional web interface and its own tests: assistant, briefing, browser-automation, coding, connector, dream, inbox, recap, showcases, skills, system, welcome-to-rome and workflow-studio. The coding app alone is 196 files and carries the skills Rome uses to build and verify other apps, including a 53,830-byte authoring reference and an 11,473-byte app-creation skill.
- docs/
- 102 Markdown files under one directory, organised as families: 24 decision records, fourteen architecture documents, twelve concept entries, seventeen authoring rulebooks, eighteen interface documents and four north stars, plus the 32,163-byte design system, a 15,918-byte release guide and an observability schema. At the root, VISION.md is 23,320 bytes, PRODUCT.md 9,277 and DESIGN.md carries the design tokens in its frontmatter.
- The workflow surface
- Fourteen workflow files, the largest being a 24,611-byte continuous-integration file and a 21,433-byte visual end-to-end run, plus desktop build and publish, Docker publish, SDK publish, mobile and nightly jobs. Around them: twelve development skills under .claude/skills/, 41 files of scripts including token-policy linters and a prose baseline, fifteen visual test cases with a case manifest, fourteen site plugins for browser automation, and infrastructure for Chrome, ClickHouse, OpenTelemetry, Traefik and a sync service.
Choices, and what they beat
Only the main process creates an action worker over letting a worker fork the nested worker it needs
A process forked by a worker has exactly one IPC peer, and that peer is the forking worker, so every main-owned service is unreachable from the grandchild and the gap is silent at author time — the call reaches a peer with no handler and the caller waits out the method deadline. Nesting instead travels as a root and parent execution id, every worker looks identical to main, and one budget held by main covers them all, with the cap failing the call rather than queueing it because a nested call waiting for capacity would hold every ancestor open.
A child session owns its own transcript, trace, stream and cost over relaying child events into the parent stream and rolling the usage up
Both rejected moves put child data under a parent owner, and each has a concrete failure: a stream carrying both terminals lets a child result stand in for a parent that failed after it, an inclusive parent total double-counts in global usage the moment children keep accounting rows of their own, and a child the parent swallowed is not inspectable on the sessions surface. The parent keeps a reference and one structured completion, and a total covering descendants is walked at read time instead.
The hook recursion guard follows the causal chain across boundaries over treating a queued or cross-process job as a fresh execution context
A process boundary is exactly where a loop stops being visible: a hook reaches an action worker, the worker reaches back into main for an agent turn, and that turn dispatches hooks again. A guard that resets there measures a fragment of the chain and reads a runaway loop as a series of short healthy ones. Root id, depth and the identity of every hook already entered travel with the work instead, and the budget belongs to the process rather than to any app that shares the runtime.
On touch, a square control grows its own box to the target size over extending the control’s hit area past the box
No stacking order can put one control’s hit area beneath another control’s box, so an extended area takes taps meant for a neighbour — and a switch’s twelve-pixel reach was taking the last few pixels of the button beside it. The open change grows the box for square controls instead, keeps a vertical-only reach for labelled buttons, and documents a supported opt-out for controls packed closer than the target size.
An idle session with background work stays open over closing it on the idle timer like any other session
The sweeper closes an idle webchat session fifteen seconds after its last turn, so a task started in the background died fifteen seconds after the reply that started it. The open change keeps the session open while tasks run, bounded by a thirty-minute cap from its last activity, so a long task can finish and report without the session that owns it disappearing underneath.
Read fromVISION.md (23,320 bytes), README.md (15,123 characters), the ADRs docs/adrs/workers-never-fork-workers.md, docs/adrs/child-session-owns-subagent-stream-and-cost.md and docs/adrs/hook-recursion-chain-crosses-queue-boundaries.md, docs/concepts/sessions.md, docs/concepts/skills.md and docs/concepts/agents.md, docs/authoring/architecture.md, docs/authoring/github-issues-task-spec.md, AGENTS.md, DESIGN.md, the descriptions and review threads of pull requests 593, 592, 590, 589, 586, 585, 584, 583, 581, 579, 573, 572, 568, 567, 566, 564 and 571 with issues 591, 588, 580, 575 and 565, the per-directory size summary, and the complete 3,421-file tree with sizes.
Build log
6 stages- 01
Five weeks, 352 commits, and a version line already past 1.1.130
The repository was created on 2026-08-23, three seconds after its own first commit, and the five and a half weeks that followed took 352 commits — 100 in the remaining days of August, 247 in September and five in the first hours of October. Eighteen accounts are on the contributor list and one dominates: zhangfand wrote 193 of the 352, with yunfanye on 49, Jessie-QingYu on 36, zoolsher on 34, a workflow bot on 13 and Asuka109 on 10, and every commit carries a linked account. 114 of the 215 co-author trailers name a Claude model or Claude Code — Opus 5 on 67, Fable 5 on 16, Fable 5.1 on 15, Opus 4.6 and Claude Code on six each, and three more on the million-token and 4.8 lines — while 74 name a person, most often the maintainer himself. Releases are per package rather than per product: the twenty the report captured run from 2026-09-10 to 2026-09-25 across seven package names, among them app-runtime 0.6.5 to 0.6.7, ui 0.2.7 to 0.3.3, rome-web-components 0.1.14 to 0.1.19 and a first host-helper 0.1.0. The image tags are a separate and faster line, the twenty newest running v1.1.111 to v1.1.130. Around all of it sit 661 stars, 56 forks, seven watchers and 83 open issues.
- 02
What compounding means here, and what it does not
The claim is written down before it is built. VISION.md, 23,320 bytes, is titled Rome: an OS for recursive agents, and it argues that model scaling is the axis everyone measures while the environment is the one that compounds: “Models scale intelligence. Rome scales the environment that intelligence can use.” It names the loop it aims at — create, discover, compose, adapt, preserve, evaluate, improve — and concedes that Rome can create, discover and compose today, with persistent capability formation still ahead. The README puts the same claim in a table where what accumulates is “actions, skills, and apps as git-tracked code, plus memory and app-private data”, and draws its sharpest line against Hermes Agent because what persists there “is text that informs the next reasoning run” while Rome persists software. The tree follows: capability-discovery.ts with a route over it, a flat skill catalog whose skills belong to apps and load on demand, actions as the executable unit with a 61,425-byte engine, and memory as files with a git source behind them. Two first-party apps exist only to tend that pile — dream, whose skill-review action reviews the skill set, and skills, which imports one. That is the difference from this archive as well: the nearest records keep what an agent learned as text, while Rome argues the durable unit is a capability that executes.
- 03
Three written rules that bound the recursion
Recursion is bounded by three decision records, all dated 2026-08-11, before the repository’s first commit. The first governs delegation: a subagent runs in a child session that owns its transcript, trace, stream, status and accounting; the parent stores only a reference plus one structured completion. The rejected alternative comes with its failure — relay child events into the parent stream and a child that finished before its failed parent becomes the last result standing, so the child answer is published as the parent answer. The second governs processes: only main creates an action worker, a worker needing a nested action asks main to run it, and nesting travels as rootExecutionId and parentExecutionId rather than as operating-system parentage. Forking a grandchild from the worker is rejected because that child holds one IPC peer holding none of the main services, so the miss surfaces as an expired deadline in an unrelated call. One budget held by main covers every worker at any depth, and the cap fails the call rather than queueing it, since a nested call waiting for capacity holds every ancestor open. The third governs hooks: root id, depth and every hook already entered travel with the work the hook causes, across async boundaries, worker processes and the turns those workers start; an over-budget hook is a logged skip with telemetry that never fails the causing turn.
- 04
Whose turn it is: steering, forks and a model pin
Turn ownership is where the runtime spends its effort. One open pull request moves the rules for whose turn an SDK message belongs to, which results end a turn and which send ids stay tracked, out of the provider and into one small class, because they had been spread across closure variables; the same author reports thirteen probes against SDK 0.3.281 to test an ordering the SDK never produced. A second gives reasoning, a long tool input and a command output the streaming treatment and the block identity text already had, since a live preview had been matched to its block by event order alone; its third review round declines an extra derivation of a completed block index, naming the SDK behaviour that would have to change first. Model resolution is settled conservatively: a session remembers the model that produced its history, and if that model cannot run — logged out, quota-exhausted, entitlement lost — the turn fails with a structured error instead of silently substituting another, and an explicit selection is the rescue path. Forks are the other half of the same idea: isolated by default with an empty tool surface, exact when a caller needs the source prefix reproduced, one-shot or continuable, branchable only from a turn whose provider checkpoint was persisted, and unable to write a pin onto the source.
- 05
The failures the authors wrote down
The recorded defects are specific about how long they lived. An open issue reports that a failed dream run is recorded only in an action execution table, with nothing in the interface showing it, so dream:skill_review failed on every run for two and a half months and 212 runs before anyone noticed. Another open issue, filed by the maintainer against his own code, says a Claude result whose subtype reads success while its error flag is set is published as the turn answer and delivered to channels, because the terminal is decided from the subtype alone. A closed follow-up records a background pnpm app:install that returned at once being counted as a successful install, and the detector problem underneath it: current Codex serializes a command item as exitCode while the web read exit_code, so on Codex the install never counted, and on Claude it never could, because Claude output carries no exit code. A closed fix records that migration 0037 renamed the webchat session tables and Showcases kept querying the old names, so the session picker failed with no such table. Another records a dependency bump that added a 300-pixel table height default the Markdown wrapper never set, clipping every table longer than about ten rows. A switch whose hit area reached twelve pixels past its track took the last few pixels of the Run button beside it, found while answering an unrelated review.
- 06
The process turned on the process
A repository this young still writes its rules down, and then lints them. docs/authoring/ holds a rulebook whose families carry tier tags for mechanical, model and human obligations, where a sentence is admitted only if a plausible diff would otherwise violate it and a refactor preserving behaviour could not make it false, and where a sentence that stops clearing that bar leaves rather than staying for safety. Architecture docs are forbidden from citing file paths at all. Prose is checked by nine Vale rules under .vale/styles/Rome/, run through pnpm lint:prose against a CI-pinned linter version. Issue shapes live in docs/authoring/ rather than in an issue template directory — a task spec needs Situation, Scope and Acceptance, carries the task label, and earns a second label when an agent can implement it from the body alone without the conversation that produced it, with each acceptance item naming what proves it, a committed test or one run against the finished branch. The repository then uses that machinery on itself: .claude/skills/ holds twelve skills for its own workflow, among them respond-to-review, file-issue, babysit-pr, loop-northstar and loop-reconcile, with another that turns a working list into a project board, and the sampled pull requests are one maintainer reviewing himself in numbered rounds that list what was fixed and what was declined with a reason.
Adjacent records
All records →No. 105
OpenBot
An open-source platform from CopilotKit that gives each AI coworker a computer of its own — a container with Chromium, a workspace volume and its own logins — where a coworker is any endpoint speaking AG-UI, and every browser, file, MCP or shell action passes through one gateway that decides it against a CEL policy, writes an audit row and only then acts, or refuses and names the rule.
No. 120
Easel
An open-source content workbench for social media creators: one agent runs the whole loop — aggregate the hot lists, plan a topic, generate the copy, the cards, the voice and the video, publish the finished file to an account that is already logged in on seven Chinese platforms, then read the numbers back into the account profile that shaped the next round.
No. 113
Chat On Steroids
A desktop workspace that gives ChatGPT local tools over MCP — files, a shell, terminals, the desktop — plus a companion Chrome extension that drives the user’s own ChatGPT conversation through a tunnel, so the model works on a real project while the app records every tool call and moves the session into a fresh chat when the old one fills up.