Tutorial 01 First App - developerz-ai/ultimate GitHub Wiki
One command scaffolds a monorepo that already runs. No Docker daemon, no database to provision, no .env to fill in.
As of 2026-08-23, with every gate row re-measured As of 2026-09-12. This page documents main, not a published release. Every file count, gate result and command output below was re-measured on this tree: x new โ bun install โ the command, in a directory outside the checkout. Rows that still describe a published version say so in their own line.
Series: 1 ยท 2 โ first feature ยท 3 โ auth and admin ยท 4 โ jobs and realtime ยท 5 โ deploy free ยท 6 โ growing up
| Need | Version | Check |
|---|---|---|
| Bun | โฅ 1.4.0 | bun --version |
| Docker | only for tutorial 5 | docker --version |
| Postgres | never, locally | โ |
cd $(mktemp -d)
bunx create-ultimate myappUnpinned on purpose. Pinning the tutorial to a version pins readers to a scaffold that stops matching this page on the next tag; README.md and llms.txt drop the pin for the same reason. To reproduce a specific release, add @<version> and read that release's tag of this page.
161 files in /tmp/tmp.XXXXXXXX/myapp
โ created myapp โ next: cd myapp && bun run setup && bun run dev
Counts are a derived fact โ x new --dry-run --json lists every file, and one added template moves the number.
cd myapp && bun installx is a workspace dependency, not a global install. bunx x <command> resolves node_modules/.bin/x; the package.json scripts (bun run verify, bun run dev) resolve the same binary. A globally installed x (bun link, bun add -g) run inside the app hands over to that same file, As of 2026-09-05, and says so on stderr: a global copy carries its own @ultimat3/entity with an empty registry, and it wrote a zero-entity manifest under a green exit code before it did. ULTIMATE_KEEP_GLOBAL_CLI=1 is the opt-out โ @ultimat3/cli's README has the measurement.
Counts are derived, never quoted here โ x new --dry-run --json | jq '.data.files | length', per
shape. The verdicts are ci.yml's scaffold-smoke job, As of 2026-09-12:
| Invocation |
bun run setup && bun run check on run one |
|---|---|
bunx create-ultimate myapp |
green, 20 of 20 steps pass โ budgets among them |
bunx create-ultimate myapp --no-example |
green, 20 of 20 steps pass โ one fewer route to weigh |
--no-example omits the post feature slice and its route. Pick it when an agent is about to write the real feature anyway โ tutorial 2 starts there. Pick the default when you want a worked example to read.
bun run checkThe app's own gate: x build --target static, then x verify. The build first, because the
budgets step weighs .x/build-stats.json and that build is its only writer.
--no-example, verbatim, from ci.yml's scaffold-smoke job on an ubuntu-latest runner,
As of 2026-09-23 (CI run 35892087181, main at 9b8feafc) โ the whole gate, in the order it runs:
โ typecheck
โ lint
โ boundaries
โ filesize
โ package-shape
โ errors
โ unit
โ contract
- live
- job
- e2e
โ eval
โ drift
โ contract-diff
โ budgets
โ seo
โ i18n
โ policy
โ manifest
- roadmap
wall time
โ bun run setup 6911ms
โ bun run check 6474ms
โ bun run setup in 6911ms, bun run check green in 6474ms โ 20 of 20 steps pass
- is skipped, not passed: a --no-example app has no live query, job or e2e test yet, so those
steps have nothing to weigh, and roadmap is a framework-repo step. Without --no-example, the
default scaffold ships its example slice with a live test, a job test and an e2e test, so live,
job and e2e are ticks on run one โ the same CI job shows all three passing on that app. contract is not among them โ the scaffold
ships apps/web/api/health.contract.test.ts, which is what makes it a tick on run one. The summary
names every skip, so a gate that is green because a suite does not exist says so on the one line you
read. Tutorial 2 turns more of those dashes into ticks.
Reach the gate through bun run check and budgets is a tick too, for the reason below. The list is
VERIFY_STEP_NAMES (packages/cli/src/verify-step.ts), whole or not at all: the same steps run here and in the framework repo.
budgets is green on the first pass when you run bun run check, because bun run check is
x build --target static and then x verify โ the build writes the .x/build-stats.json the step
weighs. Run x verify on its own, on an app nobody has built, and you get one finding per route
with a declared budget instead โ measured As of 2026-08-23:
โ budgets 14ms
X_BUDGET_UNMEASURED (/)
cause: / declares a JS budget and no build has written .x/build-stats.json in this repo
fix: x build --target static --json && x verify --json
Three routes with --no-example (/, /admin, /dashboard), four with the example slice (plus /posts).
Not a scaffold defect, and no template change closes it โ the order of the two commands does. Every generated route declares a budget:, and the budgets step reads its measurement out of .x/build-stats.json, a file only x build --target static writes, through apps/web/prerender.ts. A bare x build defaults to docker and writes no stats. So a gate run with nothing built ahead of it reports every budget unmeasured, and running the step's own fix: is what a reader used to do by hand.
bun run check does it for you, and CI asserts the result: scaffold-smoke runs a fresh app's own bun run setup && bun run check once, with no waiver and no fix-follow (As of 2026-09), and budgets must come back green rather than merely not-red โ a skip would mean the static build bought nothing. The --allow-red budgets allowance that used to sit in that job is gone, and nothing replaced it (scripts/scaffold-gate.ts).
invariants is one callback over the whole list, and c is typed from the columns above it:
invariants: (c) => [
invariant('post_title_not_blank', c.title.trimmed().minLength(1)),
invariant('post_price_non_negative', c.price.minor.atLeast(0)),
],c.titel is a compile error that names title, not a runtime surprise. Everything x new and x g write typechecks as generated โ no !, no edit before the first x verify.
What still ships broken: Known gaps.
bunx x dev--once boots, reports and exits โ the shape to paste into a smoke test:
bunx x dev --once --port 3100{"ts":"2026-08-11T17:01:45.774Z","level":"info","msg":"ultimate web listening on http://localhost:3100"}
{"ts":"2026-08-11T17:01:45.783Z","level":"info","msg":"sync node ready","buildId":"d5912e63ac22b7e4","path":"/_x/sync"}
{"ts":"2026-08-11T17:01:45.793Z","level":"info","msg":"jobs.worker.started","workerId":"worker-019ff1c5-โฆ","queues":["default"]}
{"ts":"2026-08-11T17:01:45.793Z","level":"info","msg":"jobs.scheduler.started","tasks":0}
roles web, sync, worker, scheduler
panels routes, timeline, live, jobs, db, mail, cache, policy, manifest, services, boundaries
manifest /tmp/โฆ/myapp/x.manifest.json
introspect http://localhost:3100/_x
โ dev ready on http://localhost:3100 โ /_x mounted (11 panels), db=embedded events=embedded storage=embedded mail=embedded cdn=none
Four roles in one process, isolation simulated rather than skipped. replicator is opt-in via --role; migrate is not a dev role.
.env.development ships every service key empty, and empty means embedded:
DATABASE_URL=
NATS_URL=
S3_ENDPOINT=
PORT=3000
ROLE=web| Key | Unset means | Set means |
|---|---|---|
DATABASE_URL |
PGlite in this process, under .x/pgdata
|
that Postgres |
NATS_URL |
in-process fanout | that NATS server |
S3_ENDPOINT |
.x/storage on disk |
that S3 |
Nothing in the framework branches on if (dev) โ only the driver behind the seam differs. Same code path, same db(), same enqueue(). Full table: CLI reference ยง x dev.
myapp/
โโโ app.config.ts the one config file โ names env KEYS, never values
โโโ apps/
โ โโโ web/
โ โ โโโ site/ static, 0kb JS, SEO-critical. May NOT import app/
โ โ โโโ app/ authed, streaming, realtime. Feature slices live here
โ โ โโโ api/ actions only โ no rendering, no components
โ โ โโโ shared/ actor type, tokens, primitives. A leaf both surfaces import
โ โ โโโ server.ts the production entry: ROLE + PORT, nothing else
โ โ โโโ prerender.ts the static entry: one HTML file per `render: 'static'` route,
โ โ plus sitemap.xml and robots.txt
โ โโโ admin/ the admin app, gated on `admin:read`
โ โโโ desktop/ mobile/ README stubs, no code
โโโ packages/
โ โโโ db/ schema.ts (the entity export list โ the package's public
โ โ surface, not the generator's input) + migrations/
โ โโโ domain/ pure types, no I/O
โ โโโ i18n/ catalogs/<locale>.json โ one flat file per locale
โ โโโ mcp/ the app's own MCP surface over its actions
โ โโโ ui/ app components on semantic tokens
โโโ docker/ Dockerfile, .dockerignore, dev + prod compose, helm/
โโโ guards/ one rule per file, run by `x verify`'s boundaries step.
โ Every name in SHIPPED_GUARD_NAMES ships; `x doctor`
โ lists any missing. Delete one by deleting its file
โโโ bin/ setup, dev, check
โโโ AGENTS.md CLAUDE.md the two files an agent reads first
Boundaries are a build error, not a note โ but boundaries is a step of x verify, not a top-level command. bunx x boundaries exits X_CLI_UNKNOWN_COMMAND. Run bunx x verify and read the boundaries line. Full model: Project layout.
packages/db/migrations starts empty, and x db gen is its only writer. Two commands, in this order:
bunx x db gen "initial" # entities โ <id>.sql, <id>.snapshot.json, <id>.hash
bunx x db migrate # applies them, then diffs the live schema against the ledger it wrotebun run setup runs both โ bun install, an .env.development.local touch, x db gen "initial" when the directory holds no .sql, x db migrate, x db seed, then x manifest. x db migrate is @ultimat3/db's own migrator, the one ROLE=migrate runs, so nothing extra has to be installed.
Until the generate has run, x verify is red on its drift step โ correct behaviour with a runnable fix, not a defect:
X_DB_DRIFT: schema differs from migrations
cause: packages/db has a schema but no migration recorded it
fix: x db gen "initial"
That is the scaffold with the example slice, which declares one entity. x new --no-example declares none, and zero entities against zero migrations is agreement rather than drift, so its drift step is green from the first run and goes red the moment you write your first entity().
x new hand-wrote a 0000_initial.sql until 2026-08 and that was the defect โ a second writer of a directory the generator owns, whose file carried no .snapshot.json, so x db migrate answered X_DB_DRIFT naming x db gen and x db gen answered X_MIGRATION_SNAPSHOT_MISSING naming version control for a file version control never had. Neither fix: now names the command that raises the other (Known gaps).
x routes on a fresh scaffold reports four file routes and no findings, plus the admin's
screens. As of 2026-10, read off the templates x new writes:
| path | surface | render | hydrate | offline | declared by |
|---|---|---|---|---|---|
/ |
site | static | never | precache | apps/web/site/page.tsx |
/offline |
site | static | never | precache | apps/web/site/offline/page.tsx |
/dashboard |
app | ssr | visible | runtime | apps/web/app/dashboard/page.tsx |
/posts |
app | ssr | visible | runtime | apps/web/app/posts/page.tsx |
The admin has no page file: apps/admin/app/admin/admin.ts declares defineAdmin(), which mounts
/admin and a list, detail and form per entity. Those rows name the call and the permissions that
gate them instead of a file โ defineAdmin() ยท admin:read + posts:read โ and their --json rows
carry mount. --no-example drops /posts. x routes is not a gate step either way, so a finding
here never fails x verify โ run it after scaffolding and after every x g route.
If you do hit X_ROUTE_MODE_INVALID on a page you wrote, its fix: offers two edits and only the second works: set render: 'ssr'. Solid's <Suspense> throws under this renderer at any version, and async data needs no boundary โ await it in the component (Known gaps).
| Want | Command |
|---|---|
| is it shippable |
bun run check (add --json; it reaches both halves) |
| what is wrong with my environment | bunx x doctor --json |
| what does this error code mean | bunx x errors explain X_FORBIDDEN |
| what does this app contain | bunx x manifest --json |
| what commands exist |
bunx x help โ planned ones are marked (planned) and throw X_NOT_IMPLEMENTED
|
Tutorial 2 โ your first feature: one entity, one policy, one action, one route, one test, and the five artifacts a single declaration projects.