App Creation
Takes the agent from "I want to build app X" to "a permanent git-versioned working tree exists at $REPO and the daemon is reading from it." Once that's true, hand control to AUTHORING.md for the edit → commit → install loop.
First: is this an app, or a workflow?
Most "build me something that does X" requests are workflows, not apps. Apply this litmus before scaffolding:
A workflow takes inputs, runs a sequence (transform, call actions, use
system:summonfor an LLM step), and returns a result — summaries, digests, drafting/writing, triage, monitor-and-notify, multi-source synthesis, "do X when Y". This is the default — including when the user wants to run it repeatedly and review past runs (the platform persists run history for free; authors never build that).Scaffold a full app only if at least one is true:
- It owns a data model the user edits — records they create / update / delete and return to (tracker, CRM, library, kanban). Wanting to see past runs does not count — that's free platform history, not a custom data model.
- It has more than one distinct operation — several verbs the user chooses between (add, browse, configure, approve…), not just "run it". A single trigger, even with inputs, is still a workflow.
- It has a conversational or long-lived agent — something the user talks to, or that carries memory/its own tools across turns. A one-shot "generate X from Y" is a
system:summonstep, not an agent.None of those → STOP and use
coding:workflow_creationinstead. Two smells that you've mis-split a pipeline into an app: reaching for adb:section to store past outputs, or standing up a custom agent for a one-shot generation. A workflow is a verb (do this, give me the result); an app is a noun (a place that holds my stuff).
If — and only if — the litmus says app, continue.
Invariants this skill enforces
- Every app has a permanent, version-controlled source home. The agent picks an absolute path up front, scaffolds the template into it, then runs
git initand commits — so the bundled template lands as the first commit inside a tracked tree. There is no throwaway "session" —$REPOis where the app lives forever, and the agent can audit its own work throughgit log/git diff. spec.sourcepoints at$REPOitself withmode: "source"— the daemon owns build + pack + install as oneinstall. It runs the workspace'spnpm install+pnpm build, packs into$REPO/.rome/artifact(daemon-managed; never edit or commit it), and installs the packed artifact. Every install call must passsourceexplicitly — the daemon does not infer it from the lockfile. Subsequent installs pass the same{ mode: "source", path: "<absolute $REPO>" }.- Every generated app uses format version 2 canonical artifact ids. Definitions keep only an app-local
name(review,run,researcher); every reference or runtime call uses<app-id>:<local-name>, including same-app references. Never emit a bare artifact reference orself:<name>. Platform examples must use their real owners:system:summon,system:create_routine,system:send_message,connector:connector_proxy,core:main, andassistant:assistant. The full fixed-id table is inREFERENCE.md.
The scaffold flow
# 1. Pick an absolute path for the app's repo UNDER the "Custom app authoring
# directory" given in your Runtime Context (`~/.rome/<profile>/projects/apps`)
# — i.e. <authoring-dir>/<appId>. This tree is both the canonical authoring
# root and the runtime source of truth, and it sits under the projects root
# so the app shows up in the dashboard. Do NOT use `$HOME/projects` — that is
# off the projects tree and the app would be invisible. <appId> must match
# /^[a-z][a-z0-9-]*$/ — short, lowercase, hyphenated.
REPO="${ROME_APP_AUTHORING_ROOT:-$HOME/.rome/${ROME_PROFILE:-default}/projects/apps}/<appId>"
mkdir -p "$REPO" && cd "$REPO"
// 2. Call `system:app_management` to scaffold the template into the empty dir. rootPath is required and
// absolute — the daemon does not pick a default. It refuses a non-empty
// directory, so this must run BEFORE `git init` (a `.git/` dir counts as
// non-empty and would make the scaffold fail).
{ "op": "create", "appId": "<appId>", "rootPath": "<absolute $REPO>" }
# 3. Move the scaffolded package.json onto the current SDK release. The
# template carries a floor, not the latest — pnpm resolves each one now and
# writes back a concrete caret range. Run this BEFORE the baseline commit so
# the versions land in commit #1. Drop `@rome-os/ui` if package.json does not
# already list it: the workflow template has no web UI.
pnpm add @rome-os/app-runtime@latest @rome-os/app-web-sdk@latest @rome-os/ui@latest
# Also fill in app.yaml: `name`, `description` (written for agents), and
# `tagline` (written for people: one sentence, ≤ 80 chars / 40 CJK,
# benefit-first, like an App Store subtitle). The tagline is the share card's
# ONLY description — there is no fallback — so uncomment and fill it in, and
# update it whenever the app's purpose changes.
# 4. git init AFTER scaffolding. Initializing on top of the freshly
# materialized template means commit #1 (next step) is "the scaffold as
# shipped" and every subsequent diff is scoped to the agent's own work.
git init
# 5. Commit the scaffold as a clean baseline. The scaffold ships a
# `.gitignore` that excludes `.rome/` and `dist/`, so build/pack output
# never lands in git.
git add -A && git commit -m "Initial scaffold of <appId>"
// 6. Install. One call: the daemon runs the workspace's `pnpm install` +
// `pnpm build`, packs into $REPO/.rome/artifact, and installs the packed
// artifact. spec.source pins the SOURCE repo, not the artifact. No appId —
// the daemon derives it from the manifest and returns it.
{ "op": "install", "source": { "mode": "source", "path": "<absolute $REPO>" } }
The app is now installed. $REPO is its permanent source home; $REPO/.rome/artifact is the daemon-managed packed artifact (never edit or commit it). From here on, iterate per AUTHORING.md: edit → git commit → { op: "install", source: { mode: "source", path: "<absolute $REPO>" } } — the daemon rebuilds and repacks on every install. Always pass source — the daemon does not remember it. (Run pnpm install && pnpm build locally only when you want build errors or rome dev HMR before installing.)
Mandatory independent verification
After the app is created, committed, installed, and locally smoke-tested, delegate verification to a fresh subagent with clean context. Do not verify only in the same context that authored the app; that preserves design and implementation biases.
Run system:summon with the assistant:assistant agent and tell it to load
coding:app_verification. Pass only the verification
handoff: appId, absolute $REPO, artifact path, dashboard/API base URL if
known, the original user intent, expected happy path, and safe sample inputs.
The verifier must visit or probe the installed app at runtime and return a
verdict with evidence, issues, gaps, and suggested extra checks. It also fails
an app whose manifest has no tagline, so fill that in before handing off.
Include that verdict in the final handoff.
After setup, before writing code
Read both companion docs before editing any source file. They are split by purpose, not by topic — most features touch both:
AUTHORING.md(workflow): iteration loop, product-design rules, frontend design guideline, icon design, recurring-run pattern, boundaries, validation, delivery checklist. Read this once up front to understand what to build and when to ship.REFERENCE.md(file-level API): field-by-field meaning ofapp.yaml/action.yaml/ agent yaml; the@rome-os/app-runtimeand@rome-os/app-web-sdksurfaces; on-disk layout; theromeCLI; storage + UI rules. Re-read targeted sections any time you need a refresher on a specific file or API.
Failure modes
apps.create: rootPath is required—rootPathwas omitted fromop: "create". The daemon never picks a default; supply the absolute path chosen in step 1.scaffoldDevApp: rootPath must be an absolute path— the path you passed was relative or contained an unexpanded~. Resolve to a fully-qualified path first.App directory <path> already exists and is non-empty— either reuse the existing repo (skip steps 2–5 and jump to step 6) or pick a fresh path. A common trigger is runninggit initbeforeop: "create"— scaffold into the empty dir first, thengit init. Do not delete pre-existing contents to "fix" this — they may be the user's prior attempt.op: "install"fails becausesourceis missing — every install requiressource. Pass{ mode: "source", path: "<absolute $REPO>" }on every iteration, exactly as shown in step 6.op: "install"fails with ARTIFACT_INVALID naming a mode/shape mismatch — the declaredsource.modedisagrees with what's on disk (e.g.mode: "source"pointing at$REPO/.rome/artifact, ormode: "bundle"pointing at the repo root). The error names the exact source to pass — follow it verbatim; do not delete or restructure the directory to "fix" the shape.pnpm addin step 3 fails to reach the registry — skip it and continue. The template floor installs, andpnpm upfrom$REPOtakes the app to the current release once the network returns.op: "install"fails with "App build failed in …" — the workspace's ownpnpm install/pnpm buildfailed inside the daemon. The previously installed version keeps running. Reproduce locally withpnpm --dir "$REPO" install && pnpm --dir "$REPO" run build, fix the error, re-install.
Scope
Brand-new apps from templates only. Use coding:app_remix for a new app derived from an installed App Store app. Editing an existing installed app, the write-protection mechanism, and worktree-based forks use different flows.