Anatomy of an ObjectStack App
A tour of the metadata an AI agent writes for you — objects, actions, views, apps — and how to read it so you can verify the result in the Console.
Anatomy of an ObjectStack App
An ObjectStack app is its metadata: a few hundred lines of typed definitions for objects, actions, views, apps, automation, and permissions. In the AI-first workflow, an agent writes these files for you — you rarely type them by hand.
But you do need to read them. In AI development the human's job shifts from writing code to verifying what the agent produced: is the object modelled right, is that predicate scoped correctly, does the app match what you asked for? This page is a tour of the pieces so you can review an agent's work with confidence — and catch the things the validation gate can't.
Want to build something, not just read about the pieces? Follow Build with Claude Code — it runs the whole loop (describe → agent authors → validate → verify) on a live app. New here? The Example Apps are working code you can run today.
What you build
Everything an agent writes falls into one of a few areas — the same way the work divides when you build an app. This is the map to hold in your head when you read a project:
| Area | The metadata you author (src/…) | What ObjectStack derives from it |
|---|---|---|
| Data | Objects, fields, relationships, validation (src/objects) | Database schema, CRUD APIs, type safety |
| Automation | Flows, workflows, triggers, approvals (src/flows) | Event handlers, approval chains, scheduled jobs |
| Interface | Views, apps, dashboards, actions (src/views, src/apps) | The Console UI, navigation, responsive layout |
| Access | Roles, permissions, sharing, row-level security | Middleware, RLS policies, field masking |
| AI | Agents, tools, RAG, MCP exposure (src/agents) | Chat, search indexes, an MCP server |
The through-line: you author intent once as metadata, and the runtime derives the database, the API, the UI, and the AI tool surface from it. That's why an agent can build across all of these in one coherent change — and why reading the metadata tells you everything about how the app behaves.
Reading an object
The object is the heart of every app. Here's a representative one, in the same
ObjectSchema.create + Field.* form the scaffolder and the agent use:
import { ObjectSchema, Field } from '@objectstack/spec/data';
export const Ticket = ObjectSchema.create({
name: 'support_desk_ticket', // ← snake_case, namespace-prefixed
label: 'Ticket',
pluralLabel: 'Tickets',
icon: 'life-buoy',
description: 'A customer support request.',
fields: {
subject: Field.text({ label: 'Subject', required: true, searchable: true, maxLength: 200 }),
description: Field.textarea({ label: 'Description' }),
status: Field.select({
label: 'Status',
required: true,
options: [
{ label: 'Open', value: 'open', color: '#3B82F6', default: true },
{ label: 'Pending', value: 'pending', color: '#F59E0B' },
{ label: 'Resolved', value: 'resolved', color: '#10B981' },
{ label: 'Closed', value: 'closed', color: '#6B7280' },
],
}),
},
sharingModel: 'private', // org-wide default (OWD) — required by the security gate
enable: { apiEnabled: true, searchable: true },
});What to check when you read one:
- Names are
snake_caseand namespace-prefixed (support_desk_ticket) — the scaffolder derives the prefix from your project name. - Field types are
Field.*builders (text,textarea,select,date,lookup, …), not raw strings. Aselect'soptionscarry the exact labels, values, and colors you'll see in the UI — verify these match your intent. required,default,searchableencode behavior the UI and API inherit automatically.sharingModelis the org-wide default for records the user doesn't own — an explicit, authored security decision (privateunless you have a reason otherwise). The validation gate rejects custom objects that omit it.
This one definition powers the REST API, the Console UI, and the MCP tools exposed to AI agents. Define it once, and ObjectStack derives the rest — see Data Modeling for the full field catalog and relationships.
Reading the other pieces
The remaining domains follow the same shape — a typed factory call per file. When reviewing an agent's work, these are the two things most worth a close read:
- Actions (
defineAction) — buttons and bulk operations. Theirvisible/disabledpredicates are CEL, record-scoped:record.status, never a barestatus. A bare reference silently hides the action on every record — the single most common AI mistake, which is exactly whyos validaterejects it. See it in action in Build with Claude Code → the gate. - Views & Apps (
defineView,App.create) — the list/form lenses and the navigation. Reading these tells you what the user will actually see and click.
For the full authoring surface of each, load the matching skill or read UI Metadata and Automation.
Reading a project's layout
Metadata is grouped by domain under src/. This map lets you navigate any
ObjectStack project — including one an agent just generated:
support-desk/
├── objectstack.config.ts # defineStack() — the single entry point, wires it all
├── src/
│ ├── objects/ # Data models (required)
│ ├── actions/ # Buttons, bulk operations
│ ├── views/ # List / form / kanban lenses
│ ├── apps/ # Navigation shells
│ ├── flows/ # Automation logic
│ ├── dashboards/ # Analytics
│ └── agents/ # AI agents
└── test/ # TestsEach folder has an index.ts barrel that re-exports its metadata; those barrels
are imported into objectstack.config.ts. There is no filename-suffix magic —
metadata is wired in through those explicit imports, so objectstack.config.ts is
the one place that tells you what's actually in the app.
How you verify
Reading the metadata is half of verification; running the app is the other half. Two commands, both of which the agent runs for you but you can run yourself:
npx os validate # the gate — schema + CEL predicates + widget bindings (no artifact)
npx os dev --ui # boot the app, then open http://localhost:3000/_console/os validate proves the metadata is well-formed; the Console at /_console/
proves it does what you meant — create a record, watch an action show/hide,
confirm the nav and filters. That visual check is the human's half of every build
loop. See Validating Metadata for what
the gate catches, and Build with Claude Code
for the full describe → author → validate → verify cycle.
Go deeper
Data Modeling
Objects, fields, relationships, and validation rules — the foundation of every app.
Business Logic
Flows, workflows, triggers, and formulas that automate business processes.
Security Model
Profiles, permissions, sharing rules, and row-level security.
AI Capabilities
Agents, RAG, natural-language queries, and the generated MCP tool surface.
Next steps
- Build with Claude Code — Build a real app end-to-end with an agent
- Example Apps — Run the Todo, CRM, and showcase examples
- CLI Reference — All available commands
- Protocol Reference — Complete schema documentation