Ultimate

The eight primitives

Everything in the framework is one of these. x g resource <name> scaffolds a whole feature slice — an entity + repo, a policy, an errors module, two actions (create-*, archive-*), a live query, a job, an app route, plus service, UI and i18n files, 25 in all — each with a test that passes on the first run. Not one of each: the slice emits no mutator and no task. Those have their own generators.

entity    — a table + its domain type + invariants
policy    — an authz rule, evaluated in every surface
action    — a mutation or command (server-authoritative)
mutator   — an action with an optimistic local twin (offline/realtime)
query     — a read; optionally live (subscribable)
job       — durable background work, optionally multi-step
route     — a URL + render mode + metadata + offline strategy
task      — a scheduled trigger (cron) that enqueues jobs
PrimitiveDeclared inGeneratorDeep page
entity<feature>/entity.tsx g entity <name>Entities and migrations
policy<feature>/policy.tsx g policy <name>Policies and authz
actionapi/ or <feature>/actions/<name>.tsx g action <name>Actions
mutator<feature>/actions/<name>.tsx g mutator <name>Realtime
query<feature>/queries/<name>.ts, or live/<name>.ts with --livex g query <name> --feature <slice>Queries and live queries
job<feature>/jobs/<name>.tsx g job <name>Jobs and workflows
routea route folder's config exportx g route <path>Routes and render modes
task<feature>/tasks/<name>.ts, plus the job it enqueuesx g task <name>Scheduled tasks

Every one is a function returning a value, so an app encodes a house rule by wrapping one and exporting its own factory — tenantEntity, auditedMutator — with the registry, the manifest, the projections, admin and MCP treating the result identically. That is how the framework extends itself too (llm() returns an action, backfill() returns a job), and it is why there is no plugin API → Building your own base.

Every primitive projects itself

A primitive's surfaces are methods on the primitive, never free functions taking it. publishPost.openapi(), not toOpenApiOperation(publishPost). (The MCP tool is @ultimat3/mcp's one projection, toolFrom(publishPost): a tier-3 primitive cannot hold a tier-4 projection.) Every declared field is lifted onto it, and the declaration object — the handle, the sql, the server — is not reachable from app code at all.

PrimitiveIts surface
entity$-sigil members: posts.$view([...]), posts.$parse(row), posts.$assert(row). An entity is its columns, so the sigil keeps the namespace clear — posts.name is a column, posts.$name is the entity
policyone signature, ({ actor, input, row, ctx }), identical on every surface. row is required and nullable, never smuggled through input
action.as() .openapi() .client() .job() .contract() .describe(), plus .input .output .policy .mcp. handle is not among them
mutatoreverything action has, plus .local() .server() .conflict .describeMutator()
query.as() .live() .client() .describe(), plus .input .policy .cache .mcp .isLive. sql is not among them
job.enqueue() .as() .describe(), plus .parse() and .idempotencyKeyFor()
routea normalized descriptor rather than methods — meta() always awaits, budget is always an object. A route declares no behaviour to project; describePages() is the one route list
task.entries() .enqueue() .describe()

The declaration lives in a private store inside the package that runs it, and nothing exports a reader for it. So there is one execution path and one authz path structurally, not by convention: a hand-rolled look-alike carrying the right kind is X_ACTION_FOREIGN / X_QUERY_FOREIGN, never a registered primitive. Registration stamps the export name in place, so import { publishPost } is the object that projects once the app has booted — there is no second, differently-named twin to remember.

Each primitive's deep page carries the full member table.

One import per authoring file

t is re-exported from every package that hosts a declaration factory. Import it from the package you declare in; never reach past that package to @ultimat3/schema. It is the same object, not a copy — configureSchemaProvider() still swaps the provider under it — and each package pins that with an identity assertion in its index.test.ts.

Import t fromWhen the file declares
@ultimat3/entityan entity
@ultimat3/actionan action or a mutator
@ultimat3/querya query
@ultimat3/jobsa job or a task
@ultimat3/maila defineMail template
@ultimat3/mcpa defineAppMcp hand-written tool
@ultimat3/aian llm — a factory over action, so it carries t too
@ultimat3/schemanothing — a plain view-schema module, where schema is the one import

policy and route declare no schema, so neither package re-exports t: a policy decides from ({ actor, input, row, ctx }) and a route from its path.

entity

A table + its domain type + invariants. The single source of the DB schema, the TS type, and the parse boundary.

AspectRule
Projects toPostgres table, domain type, migration, repo (postgresRepo), admin screen, seed factory
Ownscolumn types, defaults, invariants, tenant column
Neverbusiness logic, I/O, HTTP awareness, policy decisions

policy

An authz rule, evaluated in every surface.

policy: can('post:publish', ({ input, actor }) => ownsPost(actor, input.postId))
AspectRule
Projects toHTTP guard, live-query row filter, job actor check, MCP tool gate, admin visibility
Ownsthe yes/no and the denial reason
Nevermutate, query outside the declared repos, or return partial data (filter in the query)

action

A mutation or command, server-authoritative. Declared once, projected six ways.

export const publishPost = action({
  input:  t.object({ postId: t.uuid, notify: t.boolean.default(true) }),
  output: PostView,
  policy: can('post:publish', ({ input, actor }) => ownsPost(actor, input.postId)),
  cache:  { invalidates: [tag.post, tag.feed] },
  mcp:    { expose: true, description: 'Publish a draft post' },
  async handle({ input, ctx }) {
    const post = await ctx.posts.publish(input.postId);
    if (input.notify) await notifySubscribers.enqueue({ postId: post.id, orgId: post.orgId });
    return post;
  },
});
AspectRule
Projects toHTTP route, OpenAPI operation, typed client function, job handle, MCP tool, contract test
Ownsinput/output contract, its policy, what it invalidates, MCP exposure
Neverread headers or cookies, render, authorize inside handle, do slow work inline

mutator

An action with an optimistic local twin. local runs client-side against the local store; server is authoritative. Same input, same name, both halves in one file.

export const likePost = mutator({
  // REQUIRED (`X_MUTATOR_NOT_IDEMPOTENT`): a replay under the same Idempotency-Key answers the
  // first result instead of running `server` again.
  idempotent: true,
  // Convergent, not incremental: `local` replays on every rebase, so applying it N times has to
  // equal applying it once — `likedByMe` is what makes the second application a no-op.
  local(tx, { postId }) {
    tx.posts.update(postId, (p) =>
      p.likedByMe ? {} : { likedByMe: true, likeCount: p.likeCount + 1 });
  },
  async server(ctx, { postId }) { return ctx.posts.like(postId); },
  conflict: 'server-wins', // | 'last-write-wins' | custom(merge)
});
AspectRule
Projects toeverything action does, plus a local-store transaction and a rebase entry
Ownsconflict strategy
Neverlet local do I/O, randomness, or Date.now() — it must be replayable

query

A read; optionally live (subscribable).

export const liveFeed = query({
  input: t.object({ orgId: t.uuid }),
  policy: can('feed:read'),
  live: true,
  sql: ({ orgId }) => db.posts.where({ orgId }).orderBy('createdAt').limit(50),
});
AspectRule
Projects toHTTP GET, typed client hook, live subscription (live: true), cache entry with tags, MCP read tool
Ownsshape + row-level filtering
Neverwrite, enqueue, or send mail. live: true requires a deterministic, bounded sql

job

Durable background work, optionally multi-step. idempotencyKey is required by the type.

export const onboardOrg = job({
  input: t.object({ orgId: t.uuid }),
  tenant: ({ orgId }) => orgId,               // the org this run acts as
  idempotencyKey: ({ orgId }) => `onboard:${orgId}`,   // REQUIRED by the type
  retry: { attempts: 5, backoff: 'exponential' },
  async run({ input, step, ctx }) {
    const org = await step.run('provision', () => ctx.orgs.provision(input.orgId));
    await step.run('welcome-email', () => ctx.mail.send(welcomeEmail, org));
    await step.sleep('3d');
    await step.run('nudge', () => ctx.mail.send(nudgeEmail, org));
  },
});
AspectRule
Projects toqueue row, per-step persistence, retry schedule, dashboard entry, MCP jobs.inspect tool
Ownsretries, steps, concurrency class
Neverassume it runs once — assume at-least-once. Durable business state lives in your tables, never only in the payload
Factories over itbackfill(), rollup(), refreshView() (Rollups), consumer() (a job plus its .supervisor task — Consumers), purge(), exportRows(), importRows(), notifier(), webhook() (outbound) and webhookReceiver() (inbound: a job plus the plain route that verifies, enqueues, then acks) — Jobs and workflows

route

A URL + render mode + metadata + offline strategy.

export const config = defineRoute({
  render:     'isr',                  // static | isr | ssr | stream
  revalidate: { tags: [tag.post] },
  prerender:  () => db.posts.slugs(),
  offline:    'precache',             // precache | runtime | network-only
  hydrate:    'visible',              // idle | visible | interaction | never
  budget:     { js: '40kb' },
  meta:       ({ post }) => ({ title: post.title, description: post.excerpt,
                               og: { image: post.cover }, ld: ld.Article(post) }),
});
AspectRule
Projects torouter entry, prerender list, sw.js precache/runtime rule, sitemap + RSS row, <head> + JSON-LD, per-route budget check
Ownsrender mode, hydration timing, metadata, offline strategy
Nevertouch the DB directly, hold business logic, or omit meta.description in site/ — that is a build error, X_SEO_META_MISSING — the same code for a missing title, with cause naming the field

Render modes: static (built once), isr (static + background regen), ssr (per-request), stream (shell flushed instantly, holes streamed — default for app pages). Four, declared once in @ultimat3/core as RENDER_MODES; spa was the fifth until 6.0.0. Table in Routes and render modes.

task

A scheduled trigger. Enqueues jobs; never does work itself.

export const nightlyDigest = task({
  cron: '0 3 * * *',
  tz: 'UTC',
  enqueue: () => [[sendDigest, {}]],
});
AspectRule
Projects toscheduler entry (lease-row leader, x_scheduler_leader), next-run introspection, a tasks row in x.manifest.json
Ownscron expression + explicit tz
Nevercontain a handler body. If it does work, it is a job

Composition

entity ──> policy ──> action ──> job ──> task
   │          │         │
   │          └──> query (live) ──> route
   └──> mutator (action + local twin)

Feature slicing puts a feature's primitives in one folder, not one layer per app:

apps/web/app/<feature>/{entity,repo,service,actions,live,jobs,policy,ui}.ts

Every primitive emits its test beside it, and that test passes on the first run — a generator that emits a TODO has moved the work, not done it. What it pins is the distant invariant the primitive owns: a policy denial, an idempotency key, a budget. Extend it as the feature grows (Testing). Every primitive appears in x.manifest.json, so x manifest --json and the MCP manifest.read tool describe the whole app as data.

The rule

If a feature doesn't fit a primitive, it doesn't ship. No ninth primitive, no escape-hatch mode: option, no plugin API in 1.0. Removing an alternative is a feature — ambiguity is the tax agents pay. Source: docs/idea/02-primitives.md.

Edit this page on GitHubThis page as markdown