# RUNNING.md — how things run today > **This document may be out of date.** It describes how things run today, > not how they must run. Trust it as a starting point; if you hit weirdness, > the implementation has probably moved and this file is why. Verify against > the code, then update this file when you notice drift. The principles in > [AGENTS.md](AGENTS.md) are the stable contract; everything below is > implementation detail that churns. ## Fresh checkout / worktree setup `bun run bootstrap` from the repo root — idempotent: `bun install` (whose prepare hook builds `@executor-js/vite-plugin` and `packages/react`, the artifacts dev servers fail without) plus Playwright chromium. A fresh worktree that skips it dies with "Failed to resolve entry for package '@executor-js/vite-plugin'". Our two upstream forks — `@executor-js/emulate` (service emulators) and `@executor-js/mcporter` (headless MCP client) — are consumed purely as published npm packages; nothing in this repo references them by path. There are no `vendor/` submodules. Each fork is its own standalone repo (`github.com/UsefulSoftwareCo/emulate`, `github.com/UsefulSoftwareCo/mcporter`): develop on its `main`, publish a bump, then bump the dependency here. The `emulate` skill covers the emulator publish/deploy loop. ## Dev servers - Everything except desktop/cloud: `bun run dev` (turbo, from root) - One app: `bun run dev` from its `apps/` directory - Self-host boots standalone with just env vars — see `e2e/setup/selfhost.globalsetup.ts` for the canonical recipe (data dir, bootstrap admin email/password, base URL, `EXECUTOR_ALLOW_LOCAL_NETWORK`) - Self-host Vite dev shows first-run setup by default. Set `EXECUTOR_DEV_SEED_ADMIN=1` to seed `admin@example.com` / `executor-dev-admin` instead. - Cloud needs WorkOS + Autumn; for a no-.env boot, point it at emulators — see `e2e/setup/cloud.globalsetup.ts` for the canonical recipe (the real SDKs against emulated services, PGlite dev DB) The e2e globalsetup files are the source of truth for "how do I boot a working instance of X" — read them before inventing a boot path. A desktop dev run collides with an INSTALLED Executor in three places, all of which look like something else. Give the dev run its own of each: ``` EXECUTOR_DESKTOP_USER_DATA=/tmp/executor-desktop-dev-userdata \ EXECUTOR_DESKTOP_SCOPE_DIR=/tmp/executor-desktop-dev-scope \ EXECUTOR_DESKTOP_SETTINGS_DIR=/tmp/executor-desktop-dev-settings \ bun run dev ``` - **userData** holds Electron's single-instance lock, so the second process quits at startup with **exit code 0 and no message** — the log simply stops after "starting electron app". - **The scope dir** (`~/.executor`) holds the sidecar's SQLite, owned by whoever opened it first; the app reports "Failed to open local SQLite data". - **The port** comes from `settings.json` in the settings dir; write `{"server":{"port":}}` there before the first launch. Do NOT move these by pointing `HOME` at a scratch directory. The plugins a local run drives resolve their own paths from the real home, and a synthetic one breaks them in ways that read as product bugs: Codex Computer Use fails every call with "Sky Computer Use native pipe startup failed", even with `.codex` symlinked back. Renderer edits inside a workspace package can be served from vite's dep cache rather than the source the package exports. If a change does not appear after a reload, delete `apps/desktop/node_modules/.vite` and restart. ## E2E: running, viewing, sharing `e2e/AGENTS.md` covers writing scenarios. Operationally: - `cd e2e && bun run test` boots dev servers and runs everything; `--project cloud|selfhost` narrows. `E2E_CLOUD_URL`/`E2E_SELFHOST_URL` attach to an already-running server instead of booting. - Runs land in `e2e/runs///` — `result.json`, step screenshots, `session.mp4` + `trace.zip` for browser scenarios, and the scenario source as `test.ts`. - `cd e2e && bun run serve` builds the viewer and serves the scenario × target matrix over HTTP, bound to all interfaces. It prefers port 8901 but walks forward to the next free port if that's taken (so concurrent worktrees, or a leaked previous viewer, never wedge each other) — read the printed `e2e viewer → …` URL for the actual port. `PORT=…` pins a port explicitly and fails loudly if it's busy. The built SPA is port- and mount-agnostic (relative assets + hash routing), so whatever port it lands on just works. Individual runs are at `#//` hash routes — when handing results to the user, link those directly, not the bare matrix. - `bun e2e/scripts/pr-media.ts e2e/runs//` converts a run's recording to a gif, uploads it to the `e2e-media` branch, and prints PR-ready markdown. E2E dev-server ports are derived and CLAIMED per checkout (`cd e2e && bun run ports` prints this checkout's block; see `e2e/src/ports.ts`) — each checkout hashes its repo root to a preferred block, atomically locks it, and walks to the next free block if squatted, so concurrent worktrees never collide or attach to each other's servers. `E2E_*_PORT` env vars pin ports explicitly. If a boot reports a squatted port, an old dev server leaked — `bun run reap` (repo root) lists and kills orphaned stacks. ## The dev CLI: live instances, interactively `cd e2e && bun run cli` — the same primitives scenarios use, as commands. Boot a target, mint identities, make typed API calls, drive MCP, read the emulator ledger — develop interactively, then crystallize the journey into a scenario. ```sh bun run cli up selfhost --share # boot, shared, stays up bun run cli up cloud --share # emulated WorkOS+Autumn bun run cli status # what's running, URLs, creds bun run cli identity selfhost # fresh identity (headers / cookies / creds) bun run cli api selfhost tools.list bun run cli mcp selfhost call execute '{"code":"return 1+1;"}' bun run cli ledger cloud workos # what hit the emulator bun run cli down selfhost # tear down ``` Instances persist until `down` — `up --share` IS the "touch it" handoff artifact, and the seeding direction too: boot, drive the product into a state (API/MCP/UI), hand across the URL. State files in `e2e/.dev/` mark deliberate long-lived instances (vs leaks); attach scenarios to a running instance with `E2E__URL`. ## Environment gotchas (learned the hard way) - The shell is fish, and the working directory resets between Bash calls. Use absolute paths rooted at THIS worktree; don't rely on a prior `cd`. - Don't write probe scripts to `/tmp` — they can't resolve workspace packages (`effect`, `playwright`, …). Put scratch scripts under the repo root (`scratch/` is gitignored) so bun resolves the workspace. - A fresh worktree's Vite dep-optimizer cache can serve PRE-REBASE code (symptom: behavior matching old code only in dev servers, while unit tests pass). Kill the server, clear `node_modules/.vite` / `.tanstack`-adjacent caches, reboot. - `bun.lock` conflicts on rebase: take either side, re-run `bun install`, never hand-merge. - Long-lived demo servers you left up for the user look like leaks to cleanup tooling — `e2e/.dev/.json` marks deliberate instances; check it before reaping, and `bun run cli down ` is the clean teardown.