15 modules that already know about each other. You stay in charge of every change.

MAXSTACK is a TypeScript app framework for building with a coding agent. Your database, admin UI, forms, validation, REST API, auth and MCP tools all derive from one typed spec — so an agent describes the product instead of hand-writing the plumbing.

Add auth, billing, teams and audit — 15 modules in all — one command each, prerequisites resolved for you, and every one of the 4,047 possible combinations proved to install identically. Nothing an agent proposes counts until you accept it, and nothing the platform regenerates touches a file you have taken over. Fast on day one. Still fast on day fifty — measured, on a project that got there.

$ npx maxstack start "a task tracker for a small team" copy
Canonical 2026 stack — React Router · React 19 · Drizzle · Postgres · Zod 4 · better-auth

v0.11.12 on npm · pre-1.0: the spec vocabulary and CLI can still change between minor versions. What is held stable is the part you would notice: the platform never overwrites a file you have taken ownership of, and every release is gated on that.

15modules
Installable, cross-wired, and every one of the 4,047 possible combinations proved to install identically
×1.03
What the second half of a 56-change history cost against the first — on one project, replayed through the real path
0
Files overwritten that the platform did not own, across every benchmark and every replay
6.7
Working artifacts derived per declaration — one typed line naming a table, a field or a page

Measured by maxstack eval across 11 benchmarks — every one green, every one byte-deterministic. Leverage runs 4.1–8.8 across the set, so the median is the middle of a real spread, not its ceiling. Every figure on this page is generated from run 805cfebf73a1 (2026-08-07).

The claim · measured, not asserted

Fast on day one. Still fast on day fifty.

Anything can generate an app once. The speed that matters is whether change number fifty still takes an afternoon — which is a property of the platform, not of the model. So we built the project that gets there and measured it: 56 changes on one app, in the order they happened, each one replayed through the real operation and regeneration path. Cost per change over the first half: 1.23. Over the second half, with roughly twice the pages: 1.27. The second change stayed as cheap as the first, and so did the fifty-sixth.

The requirement this was built to satisfy “I want to give some high-level requirements and get an actual production-level app.” — the note that started the project, before any of it existed.

  • 01
    A typed source of truth

    Code, DB, admin UI, forms, docs, and tests all derive from validated, typed artifacts — Payload CMS, generalized to whole applications.

  • 02
    AI at the framework level

    Every project ships an MCP server and skills, so any agent already knows how to extend it — no bespoke integration.

  • 03
    A living human-interaction model

    Structured decisions, review-first, provenance on every artifact, safe change — not a chat that silently overwrites your code.

  • 04
    Harnesses and evals

    An instrumented factory measures how fast and correctly AI builds and changes apps, so improvement is evidence-driven, not vibes.

An agent on a blank repo

  • Hand-writes the schema, admin, forms, API, and auth — every time
  • Spends its context window on plumbing instead of your product
  • Regenerates over the file you edited, silently
  • Gets slower with every feature, as the surface it must hold grows

MAXSTACK

  • Writes one typed line; the platform derives the rest — a median 6.7 artifacts per declaration
  • Context goes to your product, because the plumbing is a runtime, not a file
  • Every change is reviewed before it counts, and the platform never overwrites a file you took over — so change 56 is as safe as change 1
  • Plain code on a standard stack — eject any file, own it forever
The catalog · 15 modules

Pick what your product needs. They already know about each other.

A starter kit hands you a fixed set to delete from, wired on day one and yours to maintain from day two. These install through the same validated operation an agent uses, resolve each other's prerequisites topologically, and carry versioned upgrade codemods — so a module can move forward under an app that is already live. That last part is the whole difference, and breadth is what makes it visible: nobody feels an upgrade path on a product with six moving parts.

And the combinations are tested, not assumed. 15 modules is 4,047 prerequisite-closed combinations; every one of them is installed in several valid orders and asserted byte-identical, so two people who added the same modules in a different sequence are running the same app. The catalog is capped at 16 for exactly that reason — breadth is bounded by what the gate can prove, not by ambition. The combination gate · the upgrade gate.

A

Authentication

Sessions and password login on better-auth, plus the role field every other module gates against.

stands alone

$ maxstack add auth
§

Audit log

An append-only log of who did what to which record — the sink several other modules write into.

stands alone

$ maxstack add audit
E

Transactional email

Transactional email on React Email and Resend, templated from the same typed artifacts.

stands alone

$ maxstack add email

File storage

Declared file fields: per-field MIME allowlists and size caps, expiring viewer-bound reads, image derivatives.

stands alone

$ maxstack add storage

Scheduled work

Background and recurring work as a declaration, with a handler stub you own and a job table you can read.

brings auth with it

$ maxstack add jobs

Webhooks

Signed outbound deliveries with retries and a delivery log, subscribable per event.

brings auth + audit with it

$ maxstack add webhooks

Observability

Structured request logs, traces and health endpoints, wired without you touching a route.

stands alone

$ maxstack add observability

Data compliance

Data-subject export and erasure, retention windows, and the consent record behind them.

brings auth + audit with it

$ maxstack add compliance

API keys

Programmatic access scoped per resource and action — enforced in the permission layer, not at the route.

brings auth with it

$ maxstack add api-keys

Feature flags

Declared feature flags, targeted server-side, retired through a first-class op, provably unable to affect generation.

brings auth with it

$ maxstack add flags
M

Organizations & members

Teams, invitations and multi-tenant membership on top of the auth substrate.

brings auth with it

$ maxstack add members

Preferences

Typed per-user and per-organization settings, with the settings form derived from them.

brings auth + members with it

$ maxstack add preferences

Notifications

In-app and emailed notifications that respect each recipient’s declared preferences.

brings auth + email + preferences with it

$ maxstack add notifications
B

Billing & entitlements

Tiers, subscriptions and entitlement checks, with pricing as a typed op on the spec.

brings auth with it

$ maxstack add billing

Admin metrics

A metrics dashboard over auth and audit, entitlement-gated at runtime.

brings auth + audit with it

$ maxstack add admin
The workbench · you are the one who accepts

An agent proposes. You decide what becomes true.

Every entity, field, page and pricing tier carries its provenance — suggested, accepted, or manual — and generators ground only on accepted ones. So an agent can propose freely and nothing it proposed is in your app until you said so. That is a structural property, not a setting: there is no mode in which a proposal skips the queue.

A queue, not a diff you scroll past

Proposals arrive as reviewable rows with the reason they were made. Accept, reject, or clear a batch — and a batch refuses to include a risky row rather than sweeping it along.

Decisions are recorded, not remembered

An append-only decision ledger keeps the rationale next to the change. When the same question comes back in four months, the answer is in the repo.

You can see what you now own

The ownership pane names every file you have taken over, what it was derived from, and how far behind it has drifted — information, not a demand.

Nothing bypasses it

Every path that can write the spec — CLI, MCP, the workbench itself — is declared and covered by an invariant test, so a new way to change your app cannot appear unattributed.

localhost:3000/workbench
The /workbench screen: a spec-zoom panel showing entities, pages and pricing tiers each tagged suggested, accepted or manual; a review queue of three proposals with Accept and Reject buttons; and a decisions panel with one open decision awaiting a rationale.

Three proposals waiting, one open decision, and every row in the spec panel tagged with where it came from. This is the whole surface — there is no second place a change can land from.

Architecture · five layers, one monorepo

A single turborepo on the committed 2026 stack.

The stack is fixed on purpose. Swapping a piece takes a written decision record and a migration plan — never a fresh repo, which is the point: the whole product is a bet that settled decisions stay settled.

L1
Specpackages/spec
Three coordinated typed sources of truth: the product layer (PRD as a graph of small typed entities), the data layer (typed entities and fields, which the runtime materializes as a Drizzle schema enriched with withMeta), and the page/UX layer (a typed template registry). Provenance flags, an append-only decision ledger, and typed spec-ops are in the base types from day one.
L2
Runtime · Sproutpackages/maxstack-core
The kernel. Add an entity to the spec → get admin CRUD, forms, data tables, REST, validation, RBAC, and MCP tools. The app composes at runtime; ownership is a ladder of escape hatches so regeneration is safe by construction and most change never touches generated code.
L3
MCPpackages/mcp
Sprout auto-generates RBAC-gated CRUD tools per resource; platform tools (query / propose / apply spec change, run generators, run checks, record decision) sit alongside. Skills are thin wrappers — any agent gets identical capability over one POST /mcp endpoint.
L4
Harnesspackages/harness
maxstack eval runs spec → generate → change → validate headlessly and records the numbers that gate decisions — above all the change-expressibility ratio and regeneration-safety. Evidence, not assertion.
L5
Feature bundlespackages/features
A bundle = runtime (schema + pages + DI) + its own eval artifacts + prerequisites, installed through the same validated spec-op path an agent uses. Versioned, with upgrade codemods. Auth, billing, email, members, audit, admin metrics.
L2 · The Sprout engine

Add one entity. Grow a whole admin surface.

One spec-op is the seed — that is the whole authoring surface, and it is what an agent writes. Sprout builds the typed table from it at runtime and derives the entire CRUD surface from there: no per-entity code, no scaffolding to maintain, and no schema file to keep in sync.

what you (or your agent) write
{
  "op": "data.addEntity",
  "args": { "entity": {
    "id": "e-task", "name": "task",
    "fields": [
      { "name": "title", "type": "string", "required": true },
      { "name": "done", "type": "boolean", "required": true },
      { "name": "priority", "type": "enum", "required": true,
        "options": ["low", "medium", "high"] }
    ]
  } }
}
// validated, then appended to the op log with its author recorded

Sprout builds the table from that op, at runtime

what you get — never a file in your project
// constructed in memory by the runtime; the database is reconciled
// with CREATE TABLE / ADD COLUMN IF NOT EXISTS. Shown for shape only.

priorityEnum = pgEnum('priority', ['low', 'medium', 'high'])

task = pgTable('task', {
  id: uuid('id').primaryKey().defaultRandom(),

  title: withMeta(text('title'), {
    label: 'Title', required: true,
  }),

  done: withMeta(boolean('done'), { label: 'Done' })
    .default(false),

  priority: withMeta(priorityEnum('priority'), { label: 'Priority' }),
})

…and derives all of this from it

Admin CRUD

List / detail / create / edit at /admin, no per-entity code.

Typed forms

Labels, placeholders, and constraints straight from the metadata.

Data tables

Sortable, filterable columns derived from filterable flags.

Validation

Zod 4 schemas from min/max/required — client & server.

REST API

Resource endpoints with the same rules the forms enforce.

MCP tools

RBAC-gated per-resource CRUD tools — agents get them for free.

Add a field to the spec and it's writable on the very next request — no restart, no migration ceremony. Additive-only DDL means a change can't strand your data.

Working app, immediately

You don't wait for codegen to have a real app.

The /admin surface is grounded in your project's accepted spec. Accept a field in the workbench queue and it appears in the form on the next request — rows persist in on-disk Postgres. Generators land the route code later, on your schedule.

◈ derived live from the accepted spec — zero hand-written UI
localhost:3000/admin/task
The /admin/task screen: a sidebar listing Article, Author, Comment, Tag and Task, search and Done / Priority / Author filters, a created-at date range, and a seven-row table with Title, Done, Priority, Author and Created At columns plus Export CSV and New buttons.

Search, per-column filters, a date range, CSV export, row actions — all derived from the spec. None of it written per-entity.

Quickstart · zero to a running app

One command to an app with rows in it.

Node ≥ 22 is the only prerequisite — no account, no checkout, no build step. maxstack start takes a sentence and hands back a running app with sample data already in the tables. From there you run claude and describe the next thing; building is a conversation, not a stack of commands.

$ npx maxstack start "a task tracker for a small team"

One package — the CLI plus the prebuilt web runtime it pins (maxstack-runtime) — so start, dev, build and deploy all work from the install, with nothing to compile.

bash — ~/prj
1$npx maxstack start "a task tracker for a small team"
✔ scaffolded task-tracker · landed 4 spec-ops — origin: ai, in the op log
   ├ Project  name:text! · notes:text
   ├ Task     title:text! · status:enum · priority:enum · dueOn:date · project:->Project
▸ /admin/task · /workbench · POST /mcp localhost:3000
2$cd task-tracker && claude
> add a due date to tasks, and a page grouped by assignee
✓ agent drove the spec-ops over POST /mcp — waiting in your review queue
3$maxstack add members billing
✓ teams + subscriptions installed with their prerequisites, through the same op path
4$maxstack validate
✓ spec valid · manifest intact · regeneration safe
  1. start
    A sentence in, a running app out

    Scaffolds the project, lands the spec-ops your description implies, seeds sample rows, and serves. You land on an /admin with data in it, a /workbench to review from, and a POST /mcp endpoint any agent can drive. --no-seed if you would rather start empty.

  2. claude
    Then just talk to it

    start already wrote .mcp.json and .claude/skills, so any agent has the platform's tools from its first message. You describe the next change; it lands typed spec-ops over POST /mcp. The CLI verbs are the same vocabulary if you would rather type.

  3. add
    Pick the modules you need

    15 of them, each installed through the same validated op path — with prerequisites resolved for you, and an upgrade codemod for when the module moves forward.

  4. validate
    You stay in charge of every change

    Nothing an agent proposes becomes grounding truth until you accept it in the workbench, and validate is the standalone gate: spec valid, ownership manifest intact, regeneration safe. Trust nothing that has not passed it.

Prefer the scaffold without the starting spec, the rows, or the server? maxstack init is exactly that, and maxstack demo seeds sample data into an existing project.

The ownership ladder

Always reach for the lowest rung that expresses the change.

Every change goes up an explicit ladder — cheaper rungs absorb more, and regeneration stays safe by construction. The weight is what the change cost you; the platform's job is to keep changes on the low rungs.

1

Spec-op most changes · zero hand-written code

Tell the agent "add a renewsOn date field to Subscription." It applies data.addField over MCP; the field appears in your review queue; you Accept; the admin form has it on the next request and every regeneration derives it everywhere.

1
weight
2

Regen-as-diff spec touches generated files

regenerateAsDiff re-derives and returns a reviewable unified diff instead of overwriting. Protected files are surfaced, never proposed. The batch lands only if the validate gate passes.

2
weight
3

Slot-fill custom UI inside a generated page

Generated pages render <Slot name="…"/> wired to a stable, user-owned *.slots.tsx file. The generator wrote the stub once and never touches it again — edit it freely. The generated file keeps regenerating; your slot survives byte-identical.

3
weight
4

Eject you own the whole file, forever

Eject copies the generated file, strips the banner, and flips its ownership to ejected in the manifest. From then on regeneration never overwrites it — it's yours, plain code on a standard stack, including the responsibility to keep it current.

5
weight

What that guarantee measures out to: across every benchmark and every one of the 56 changes on the long-lived project, 0 files the platform did not own were overwritten — and 6 files taken over by hand on that project (2 ejected, 4 filled slots) held their bytes exactly through every later change.

The invariants that make it safe: additive-only spec-ops + additive DDL · manual rows survive regeneration · generators ground only on accepted entities · a writer that checks the ownership manifest before every write, so it can never clobber — overwrite a file you had taken over. Every change ends with maxstack validate.

The maxstack CLI

5 verbs you'll use. 24 in total when you want them.

You don't have to touch any of these — the agent drives the same vocabulary over MCP. They're here for when you'd rather type than talk. Every verb runs standalone from the npm install: dev serves the runtime that ships with the CLI, build vendors it into your project. The 5 marked everyday below are the ones worth knowing; the rest are situational, and the reference is generated from the command tree so it cannot drift from this count.

maxstack start everyday

A sentence in, a running app with rows in it out — scaffold, land the implied ops, seed, serve. --no-seed, --no-dev.

maxstack init

Scaffold a project — spec.json, config, generated app/, durable runtime dir. --desc, --backend pglite.

maxstack op

Apply a typed, additive spec-op from the CLI — the same vocabulary agents use over MCP.

maxstack add-entity

Terminal-native sugar for a data.addEntity op — --field title:text! --field done:bool, then --accept --gen.

maxstack add-field

Add one field to an entity — sugar for a data.addField op, e.g. add-field task dueOn:date!.

maxstack add-page

Add a default list page for an entity — sugar that compiles to a page.addPage op.

maxstack gen everyday

Regenerate the app tree from the accepted spec. Never-clobber: unchanged spec ⇒ all-unchanged output. --upgrade redraws against the current framework generators instead of the pinned ones.

maxstack add everyday

Install a feature bundle (auth, billing, …) through the validated spec-op path, with prerequisites.

maxstack eject

Take whole-file ownership of a generated route by id. Ownership flips to ejected in the manifest — never re-clobbered.

maxstack validate everyday

The standalone gate: spec valid · manifest intact · regeneration safe. Trust nothing that hasn't passed it.

maxstack dev everyday

Run the app — /admin, /workbench, and POST /mcp on localhost:3000. Regenerates on boot and watches the spec.

maxstack demo

Seed sample data into the project's data dir so there's something to explore before you touch the spec.

maxstack build

Structurally clone the workspace into .maxstack/runtime/ — a self-contained, vendored runtime.

maxstack deploy

Ship it — docker run or Fly, the owned slots executing in the built runtime.

maxstack review

What needs you, in order — worst first, with the reason why. Bulk accept/reject and undo; --section exposure for what's publicly reachable.

maxstack doctor

Report what is actually running — CLI and runtime versions, staleness, store lock, dev server, MCP reachability.

L4 · The harness · evidence, not vibes

The north star is change-safety — and it's measured.

Time-to-Nth-safe-change: the effort and success rate of landing a change to an already-live app with the regeneration-safety suite still 100% green. This is the one number the whole platform exists to move.

Change kindWeightWhat you did
spec op · apply-op1Expressed it as a typed op; platform generated it
spec op · regen-diff2Reviewed an agent-produced diff
slot fill3Wrote code into a cross-file extension slot
eject5Took whole-file ownership, and the upkeep that comes with it
off-surface8Built it outside the spec entirely; the platform absorbed none of it

Cheaper = more absorbed. The ordering op < diff < slot < eject < off-surface is the invariant — the moat, in cost terms.

weightPerSafeChange · the long-lived project

56 changes, one project, in the order they happened
──────────────────────────────────
first half 1.23  →  second half 1.27  (×1.03)
regen-safety 100% determinism · byte-eq regeneration sub-linear no dangling slots

Two non-negotiable gates run every maxstack eval: determinism (same spec ⇒ byte-identical generation) and regeneration-safety (nothing you own gets overwritten, zero violations). A safety violation exits the nightly eval non-zero.

And one number we publish against ourselves. Expressibility is the share of a frozen backlog of 119 real product asks that the platform absorbs as a typed operation rather than as code you have to write and then own: 0.71 today, up from 0.59 the day that backlog was frozen. It will never reach 1.00, and we would not want it to — the same backlog contains work that should stay hand-written, which puts a ceiling of 0.75 on the figure. Against the headroom that ceiling actually leaves, the platform has captured 0.74; the target is 0.80. Separately, 22 asks in the live backlog have no operation or slot at all — a published gap, not a rounding. The frozen backlog can only rise by the platform getting better, never by the corpus getting easier.

Run 805cfebf73a1 (2026-08-07): 11 benchmarks green · cost per safe change 1.58–2.40 across the corpus, 1.29 over the long-lived project.

Sketch of the deployed front page — technews ships standalone, so this one isn't a capture. The /admin and /workbench shots above are.

Dogfood · technews

Built start-to-finish through the platform's own surfaces.

technews is a Hacker-News-shaped app assembled entirely through agent spec-ops, human review ops, and generators — then run standalone via maxstack build and deployed. An owned submit slot and a ranked front page execute in the built runtime. Every surface it uses is the same one you get from the npm install:

$ maxstack init technews && cd technews && maxstack demo copy
Scope · what this is not

The honest boundaries.

Every one of these is a real limit today, not a coming-soon. If one of them is your requirement, this is the wrong tool and you should know that from the page rather than from the install.

not this

Not mobile

Web only — React Router on the server, rendered in a browser. There is no native target and none planned.

not this

Not a hosting product

maxstack build vendors a self-contained runtime into your project and deploy wraps Docker or Fly. You bring the infrastructure and the Postgres.

not this

Not a design tool

You get a coherent admin surface derived from the spec, not a canvas. Bespoke UI is a slot you write by hand, deliberately.

not this

Not in a browser tab

It starts with npx on your machine, on your filesystem, in your git history. If a hosted editor is the requirement, the builders win that comparison.

not yet

Pre-1.0

The spec vocabulary and CLI can still change between minor versions. What is held stable is ownership: the platform does not overwrite files you have taken.

not proven

No speedup multiple

There is no control arm — no "same backlog, an agent, a blank repo" measurement — so no N× faster claim appears anywhere here. Earning one is open work.

The alternatives you are actually weighing: the builders (v0, Lovable, Bolt) start in a browser tab and have no review model, so nothing stops a regeneration from landing on code you changed. The starter kits (Shipfast, Vercel’s SaaS Starter, T3) hand you a fixed set to delete from, wired on day one and yours to maintain from day two — no upgrade path. We published the module-by-module version of that comparison.

Get started

Describe what you want.
Keep every decision about it.

Node ≥ 22 and npm — the whole prerequisite list. One command scaffolds the project, lands the spec-ops your sentence implies, seeds rows and serves. From there you add modules, review what the agent proposes, and own whatever you decide to own.

$ npx maxstack start "a task tracker for a small team" copy
$ maxstack add members billing copy

init already wrote .mcp.json and .claude/skills — no claude mcp add, no hand-editing. Just run claude in the project and every agent gets the platform's tools over stdio — present in every session, no port, no ordering.

Framework before AI · Config first · No black boxes · No vendor lock-in