Nouns
A file in nouns/ declares one Noun the Startup adds or refines, with its properties, Verbs and states, and compiles to its API.
A file in nouns/ declares one Noun the Startup adds or refines. ls nouns/ lists the Startup's own API, and Nouns shows every Noun it inherits.
nouns/Lease.mdxthe Lease Noun: properties, Verbs, states; the body is its docsLease.renew.tsthe Function behind Lease.renewLease.renew.test.tsits cases
Reference
The file name is the Noun's name, capitalized as it is written in code. Frontmatter is the record, and the body is the Noun's documentation on every Interface.
| Key | Type | Holds |
|---|---|---|
extends | Noun names | The Nouns it inherits from, such as Thing or Task; several are allowed |
properties | map | Each property's type: a scalar, A | B for an enum, or a Noun name for a reference. A name ending in ? is optional |
verbs | map | The Verbs it declares beyond create, get, list, update and delete, each with its inputs |
states | list | The lifecycle, compiled from the Verbs; write it only where the compiled order is wrong |
Verb inputs
Each key under verbs is a Verb, and its value maps each input to a type. Inputs take the same types as properties, and a name ending in ? is optional. {} declares a Verb that takes only the record.
verbs:
renew: { months: integer, increase? : number }
notify: {}The record comes first, named for the Noun, so renew takes lease, months and increase. Each Verb compiles to one input type in .sb/, named for the Noun and Verb:
interface LeaseRenewInput {
lease: SB.Id
months: number
increase?: number
}$.Lease.renew checks its input against that type, and the Verb function takes it as its second argument.
Examples
Minimal
---
properties:
unit: string
---Leaseline
Leaseline tracks lease renewals for small property managers. It declares a Lease Noun.
---
extends: [Thing]
properties:
unit: string
resident? : Contact
endsOn: date
rent? : money
status: active | renewing | renewed | ended
verbs:
renew: { months: integer, increase? : number }
notify: {}
---
A Lease is one resident's agreement on one unit, from signing to renewal.What it builds
The SoftwareBuilder compiles the file. One Lease.mdx produces:
- the
LeaseNoun withcreate,get,list,updateanddelete Lease.renew, which takeslease,monthsand an optionalincrease, andLease.notify, which takeslease, each in every form:renew,renewing,renewed,renewedBy- the Events
Lease.created,Lease.renewedandLease.notified - typed SDK methods, CLI operations, MCP tools and OpenAPI paths, with types in
.sb/types/ - a screen per record and a form per Verb in every App that mounts the Noun
Routing
| Interface | Form |
|---|---|
| API | GET /leaseline.app/, GET /leaseline.app/, POST /leaseline.app/ |
| SDK | $.Lease.list(), $.Lease.get({ id }), $.Lease.renew({ lease, |
| CLI | npx startup-builder do Lease.renew --json '{"lease": |
| MCP | do with $.Lease.renew({ lease: |
get, update and delete name the record id; renew, notify and every other Verb name it by the Noun, lease. The API spec has the rule.
A Verb is a path segment and never a suffix. A record or Collection named like a declared Verb is refused at write time.
Good to know
statusholds the states.createstarts it at its first value, and only a Verb or an Event moves it: nocreateorupdateinput takes it.- Nouns sit at the root even when one Product uses them, so two Products share one Noun.
- A file named for an inherited Noun refines it: its properties add to the inherited ones.
- The Function behind a Verb lives beside the Noun in Verb functions.