How generation works

Your files compile to one model that generates every output on each request. Every output has a default, and the constraints make generation possible.

Reads
every Noun · theme.json
Generates
2 outputs
Open

One model

Your four files compile to one model, and every output reads it. Nothing is copied into your repo, so no generated code drifts from the definition.

  1. Parse. The engine reads each file's frontmatter, its $type and its $id.
  2. Check. It refuses any key, name or input outside the shape, and the refusal names the fix.
  3. Compile. It builds your Startup's model on the platform's own Nouns and stamps it as a dated Version.
  4. Generate. The API routes, the typed $, the MCP tools, the CLI, the schemas, the App screens and the Docs each read that model.
  5. Serve. Every request reads the current Version, so an improvement to a default reaches you on the next request.

Change one line

Add rent to Lease:

  rent: { type: money, currency: USD }

Send the field in try, which stores nothing:

curl -X PATCH "https://api.sb/<your-domain>/nouns/Lease" -H 'Prefer: mode=try' \
  -H "authorization: Bearer $SB_TOKEN" -H 'content-type: application/json' \
  -d '{"fields":{"rent":{"type":"money","currency":"USD"}}}'
# "mode": "try", "major": false, "stored": false,
# with the Version it would become and every Interface it touches

The answer arrives before you commit, and it covers every Interface at once:

BeforeAfter
$.Leaseno rentrent?: number
REST operations1818
Admin list4 of 7 columnsRent joins before Unit, 5 of 7, with a money input

Money has one form everywhere: an amount and a currency, as the Offer writes its price. Removing renter answers "major": true, because records hold it. A rename of renter is refused once records exist, and the refusal says to change its label instead.

The defaults

Every SaaS answers the same questions. The platform answers each one once, for every Startup on it.

QuestionAnswered for you
Who sees a new record?Only those you grant: private by default
What does delete do?Records a Version, so nothing is lost
How many come back in a list?At most 200
Can a GET change anything?No. A GET on a Verb's URL previews and runs nothing
Does a retried Verb run twice?No. A Verb POST outside try needs an Idempotency-Key
Who may do what?Four Access levels: owner, admin, member, viewer
What are the methods called?get, list, find, create, update, revert, plus each declared Verb
What does your agent's script do by default?A $ call without a mode runs in try and stores nothing
How many things does a script commit?All its writes, or none
Where does the price live?In the Offer, as one amount and currency every output reads
What does a refusal look like?Typed, with its message, and it costs nothing
What does a list screen show?At most 7 columns, and one button per Verb

The two script rows apply to $ and MCP. On REST, a record write without Prefer: mode=try commits.

Overrides

The defaults are the product. An override replaces one output, never the definition, and everything else stays generated.

OverrideWhat you then maintainWhat stays generated
ThemeThe token valuesEverything
CSSYour rulesEverything; a rule can mask a later token change
A field’s inputThat one inputThe field’s API, schema and filter
A Noun’s viewThat view’s layoutRoutes, data, Verb buttons and the other views
A page or screenThat page’s or screen’s layoutTheme, Access levels, try and revert
A Function bodyWhat the Verb doesIts URL, preview, undo, button and agent call
An API route of your ownRefused: declare a Verb—

A Startup with no theme file renders in the default theme, on its Site and in every App screen. Three lines restyle Leaseclock's Site, Admin and SaaS App:

leaseclock.com/theme.json
{
  "fonts": ["Libre Caslon Text:wght@700"],
  "light": { "accent": "#8A4B0F" }
}

A printed-form face and a brass clock-hand accent, Leaseclock's own. Every primary button takes the accent. A value that could end a CSS declaration is refused, never emitted.

A page of your own returns catalog blocks with computed props, and writes no markup and no CSS:

leaseclock.com/components/NoticeBy.tsx
const block = (name: string, props: object) => <sb-block data-block={name} data-props={JSON.stringify(props)} />

export default function NoticeBy({ endsOn, days = 60 }: { endsOn: string; days?: number }) {
  const by = new Date(Date.parse(endsOn) - days * 86_400_000).toISOString().slice(0, 10)
  return block('facts', { title: 'Renewal notice', items: [
    { label: 'Lease ends', value: endsOn },
    { label: `Give notice by (${days} days)`, value: by },
  ] })
}

The renderer applies the theme and checks the props, and you maintain that one component. A screen you add to the App reads and writes through the generated API, so Access levels, try and revert still apply.

An API route of your own is refused. New behaviour is a declared Verb, which gets its URL, preview, undo, button and agent call from one block.

The constraints

The engine refuses what it cannot generate from. Each refusal buys something every output relies on:

RuleWhat it buysThe refusal
Every key is declaredEvery Interface reads the same fields"properties" is not part of a definition
Names follow one grammarThe name is the URL, the method and the screen titlePascalCase, letters and digits
Prices live in OffersThe Site, pricing and Checkout read one numberPrice has no field "interval"

The same refusals let an agent write the definition and fix its own mistakes before anything is stored:

StepYour agent
LearnRead /llms.txt and the types.
DraftWrite each file in Leaseclock's shape.
CheckSend it in try mode, which stores nothing.
FixFix each refusal.
CommitCommit a dated Version.
UndoUndo with one call.

What you write as does: is what your agent reads on the method. The platform's own Startup Noun takes the same shape, and its types need no key:

curl -s 'https://api.sb/$.d.ts?nouns=Startup' | grep -A1 'Sell it'
# /** Sell it, with its Customers, to another party · Live|Dormant → Sold · modes try|send · a legal Decision */
# sell(target: string, input: StartupSellInput, o?: { mode?: 'try' | 'send' }): Promise<Action>

Why the constraints

Made by hand, screens multiply: 2 audiences, 4 views plus 1 per Verb with input, 3 states, 2 themes and 2 widths.

NounScreens to draw by hand
Unit96
Lease120

The platform carries that count while you stay inside the constraints.

What fits

Made for products that are records and the actions people take on them. A Lease and its renewal fit; so does a Unit and its building. Inside that shape, an agent writes the definition and one person checks it.

Blog and changelog

Every Startup gets a blog at /blog, under its own theme, and a changelog. Each changelog entry is a dated Version of the definition, so the day Lease gained rent is an entry with no post to write.

Contents