diff --git a/content/docs/getting-started/build-with-claude-code.mdx b/content/docs/getting-started/build-with-claude-code.mdx index fb88fd8400..942b017eba 100644 --- a/content/docs/getting-started/build-with-claude-code.mdx +++ b/content/docs/getting-started/build-with-claude-code.mdx @@ -41,7 +41,7 @@ hand-edits generated glue, and no metadata mistake reaches the browser unchecked | You need | Why | |:---|:---| -| **Node.js 18+** and **pnpm** | Runs the scaffolder and the dev server (see [prerequisites](/docs/getting-started#prerequisites)). | +| **Node.js 18+** and npm (pnpm / yarn / bun also work) | Runs the scaffolder and the dev server (see [prerequisites](/docs/getting-started#prerequisites)). | | **[Claude Code](https://claude.com/claude-code)** (or Cursor / Copilot) | Your agent reads `AGENTS.md` + the ObjectStack skills and authors the metadata. | ## 1. Scaffold the project @@ -63,10 +63,6 @@ The scaffolder does more than copy files. It: `npm run validate` after every metadata change*. ``` - ╔═══════════════════════════════════╗ - ║ ◆ Create ObjectStack v6.x ║ - ╚═══════════════════════════════════╝ - ◆ New Environment ──────────────────────────────────────── Environment: support-desk @@ -75,6 +71,12 @@ The scaffolder does more than copy files. It: … → Installing AI skills for your coding agent... ✓ Environment created! + + Next steps: + cd support-desk + npm install + npm run dev # Start development server + npm run validate # Verify metadata: schema + predicates + bindings ``` This is why AI authoring is reliable rather than a guessing game: **the agent @@ -192,9 +194,20 @@ export const TicketViews = defineView({ }); ``` +The agent also **wires the new files into `objectstack.config.ts`** — the object +through the `src/objects/index.ts` barrel, and the action and view via the +`actions:` / `views:` arrays in `defineStack()`. There is no filename-suffix +magic: metadata exists in the app only if the config imports it, so if a +freshly-authored action doesn't show up, the wiring is the first thing to check. + This is the same metadata that powers the REST API, the Console UI, **and the MCP tools exposed to AI** — define it once, and ObjectStack derives the rest. + +Every example on this page was authored against `@objectstack/spec` 16.x and +passes `os validate` verbatim in a freshly scaffolded project. + + ## 4. The gate: `os validate` catches AI mistakes The most common way AI-authored metadata goes wrong is a mistake that **type-checks @@ -289,6 +302,43 @@ tightening on each pass until the app is right. See [How AI development works](/docs/getting-started/how-ai-development-works) for why this stays fast and safe as the app grows. + +Beyond the pass/fail gate there's a **quality rubric**: `npx os lint --score` +prints a 0–100 metadata-quality score (relationship patterns, missing options, +roll-ups, name fields). Ask the agent to keep the project lint-clean — "run +`os lint` and fix the warnings" is a perfectly good prompt. + + +## Beyond the first app — one skill per metadata domain + +The loop above never changes; what changes is **which skill the agent loads**. +The bundle ships nine, one per metadata domain, and the agent picks by task +context (the trigger table lives in your project's `AGENTS.md`). To grow the +support desk into a full application, keep describing — each row is a real +prompt you can give verbatim: + +| You want | Skill the agent loads | Say something like | +|:---|:---|:---| +| Data model — objects, fields, relationships, validations, seeds | `objectstack-data` | "Add an `account` object and link tickets to it with a required lookup. Seed three demo accounts." | +| Queries & reports over records | `objectstack-query` | "Show me the count of open tickets per priority, using an aggregation." | +| Views, dashboards, apps, record pages | `objectstack-ui` | "Add a dashboard with a metric for open tickets and a bar chart of tickets by priority." | +| Business automation — flows, approvals, schedules | `objectstack-automation` | "Every day at 9:00, escalate tickets that have been open more than 48 hours." | +| Permissions & row-level security | `objectstack-data` (security sections) | "Members should only see their own tickets; support managers see everything." | +| Formula / conditional-field logic (CEL) | `objectstack-formula` | "Make `resolved_at` required and visible only when status is resolved." | +| AI agents & tools inside your app | `objectstack-ai` | "Add an AI skill that triages new tickets into a priority." | +| REST/auth surface tuning | `objectstack-api` | "Restrict the ticket API to read-only for non-members." | +| Translations | `objectstack-i18n` | "Add a zh-CN translation bundle for the ticket object and app navigation." | + +Two habits keep this reliable as the app grows: + +- **The gate applies to every domain.** Flows, dashboards, permissions, and + translations all go through the same `npm run validate` — the agent should run + it after each change, exactly as `AGENTS.md` instructs. +- **Keep the skills current.** After upgrading `@objectstack/spec`, re-run + `npx skills add objectstack-ai/framework/skills --all` so the agent authors + against the schemas you actually run. The full catalog is documented in the + [AI Skills Reference](/docs/ai/skills-reference). + ## 7. Your app is natively AI-operable (MCP) Here's the payoff that a hand-built CRUD app doesn't give you for free: because diff --git a/content/docs/getting-started/cli.mdx b/content/docs/getting-started/cli.mdx index aa0fe3cc4a..da12e6dfa2 100644 --- a/content/docs/getting-started/cli.mdx +++ b/content/docs/getting-started/cli.mdx @@ -42,7 +42,7 @@ os generate flow onboarding # Add an automation flow os dev --ui ``` -Open [http://localhost:3000/_console/](http://localhost:3000/_console/) — you'll see the Console UI with a data browser, metadata explorer, and API documentation. +Open [http://localhost:3000/_console/](http://localhost:3000/_console/) — you'll see the Console UI with a data browser, metadata explorer, and API documentation. Sign in with the seeded dev admin (`admin@objectos.ai` / `admin123`) — `os dev` provisions it automatically on an empty database. The boot banner also prints the app's MCP endpoint (`/api/v1/mcp`) so a coding agent can connect to the running app. ### Validate & Build @@ -138,8 +138,14 @@ os dev --database file:./data/test.db --auth-secret $(openssl rand -hex 32) | `-p, --port ` | `OS_PORT` / `PORT` | Listen port (default `3000`). In dev a busy port auto-hops to the next free one; the banner shows the actual port. | | `--ui` | — | Force Console UI on (already on by default in dev) | | `--compile` | — | Force compiling `objectstack.config.ts` → `dist/objectstack.json` before starting (auto when the artifact is missing; ignored with `--artifact`) | +| `--fresh` | — | Ephemeral `OS_HOME` in the OS tempdir (clean DB, uploads, storage), auto-deleted on exit; implies `--seed-admin` | +| `--seed-admin` / `--no-seed-admin` | — | Seed a dev admin (`admin@objectos.ai` / `admin123`) on an empty DB — default on; override with `--admin-email` / `--admin-password` | | `-v, --verbose` | — | Verbose output | +By default `os dev` keeps your data between restarts in a project-local SQLite +file at `.objectstack/data/dev.db` (created on first run). Pass `--database`, +set `OS_DATABASE_URL`, or use `--fresh` for a throwaway run. + With a file-backed SQLite database, dev also provisions a sibling `.telemetry.` file registered as the `telemetry` datasource — lifecycle-classed system data (activity streams, job runs, notifications, @@ -194,7 +200,8 @@ only gate the **automatic** registration of optional plugins. The `auth` tier requires `OS_AUTH_SECRET` to be set; otherwise `AuthPlugin` is skipped with a yellow warning and the `/api/v1/auth/*` endpoints will -return 404. To take full control, set `tiers` on the stack config: +return 404. (In `--dev` mode the CLI falls back to an insecure local secret so +login works out of the box.) To take full control, set `tiers` on the stack config: ```typescript import { defineStack } from '@objectstack/spec'; @@ -250,9 +257,8 @@ exploration, package management, and runtime metadata diagnostics. └─────────────────────────────────────────┘ ``` -In this framework repo the prebuilt console bundle is served at `/_console/`. -For source-level Console UI work, run the sibling `../objectui` Vite server and -point it at a backend on port 3000. +The prebuilt Console bundle ships with the framework packages and is served at +`/_console/` — no separate frontend install or build step is needed. ### Production @@ -260,7 +266,10 @@ point it at a backend on port 3000. Boots a production server **directly from a compiled `objectstack.json` artifact** — no `objectstack.config.ts` required. This is the canonical "deploy a built ObjectStack app" -command: hand a server one JSON file (or a URL pointing at one) and it runs. +command: hand a server one JSON file (or a URL pointing at one) and it runs. When the cwd +*does* contain an `objectstack.config.ts` and no artifact exists yet, `os start` +auto-compiles it first; with no config and no artifact at all it boots an **empty kernel** +with the Console + marketplace, so you can install apps interactively. ```bash # Quick start — load ./dist/objectstack.json with sqlite at file:/data/objectstack.db @@ -297,7 +306,8 @@ os start | `-d, --database ` | `OS_DATABASE_URL` | `file:…` / `libsql://` / `postgres://` / `mongodb://` / `memory://` | | `--database-driver ` | `OS_DATABASE_DRIVER` | Force `sqlite` \| `turso` \| `postgres` \| `mongodb` \| `memory` when the URL is ambiguous | | `--database-auth-token ` | `OS_DATABASE_AUTH_TOKEN` | Auth token for libsql/Turso | -| `--auth-secret ` | `OS_AUTH_SECRET` | Secret for `@objectstack/plugin-auth`; without it `/api/v1/auth/*` is skipped (server still runs) | +| `--auth-secret ` | `OS_AUTH_SECRET` / `AUTH_SECRET` | Secret for `@objectstack/plugin-auth`. If neither the flag nor the env var is set, `os start` **auto-generates one** and persists it at `/auth-secret` | +| `--home ` | `OS_HOME` | Home directory for persistent state (default `/.objectstack` when an `objectstack.config.ts` is present, otherwise `~/.objectstack`) | | `--environment-id ` | `OS_ENVIRONMENT_ID` | Environment identifier (default `env_local`) | | `-p, --port ` | `OS_PORT` / `PORT` | Listen port (default `3000`). **Production fails loudly if the port is busy** — see note below. | | `--ui` / `--no-ui` | — | Mount the Console portal at `/_console/`. Enabled by default (so you can install marketplace apps); pass `--no-ui` to disable it. | @@ -310,7 +320,7 @@ os start > (CORS). **Pin the port explicitly** (`OS_PORT=8080 os start`) and keep > `OS_AUTH_URL` / `OS_TRUSTED_ORIGINS` in sync when you change it. -**Resolution priority (artifact):** `--artifact` > `OS_ARTIFACT_PATH` > `/dist/objectstack.json`. +**Resolution priority (artifact):** `--artifact` > `OS_ARTIFACT_PATH` > `/dist/objectstack.json` > `/dist/objectstack.json` > auto-compile from `objectstack.config.ts` (when present) > empty kernel. **Resolution priority (database):** `--database` > `OS_DATABASE_URL` > `DATABASE_URL` (legacy) > `file:/data/objectstack.db`. **What it boots:** @@ -320,7 +330,11 @@ os start - Runs standalone boot mode with one active environment. **Authentication:** -The `auth` capability is auto-loaded only when both (1) the artifact declares `requires: [..., 'auth']` **and** (2) a secret is provided via `--auth-secret` or `OS_AUTH_SECRET`. Without a secret, `AuthPlugin` is **silently skipped with a warning** — the server still boots and serves data/REST routes, only `/api/v1/auth/*` (login/register) is omitted. This is intentional: `os start` is happy to run an unauthenticated, internal-network deployment. +`os start` always resolves an auth secret — `--auth-secret` > `OS_AUTH_SECRET` / +`AUTH_SECRET` env > a secret **auto-generated and persisted** at `/auth-secret` +on first run — so `/api/v1/auth/*` (login/register) and the Console's login flow work +out of the box, without any manual secret provisioning. Set the env var (or flag) +explicitly when you deploy across multiple nodes or want to rotate the secret. **`os start` vs `os serve`:** `os serve` boots from `objectstack.config.ts` (TypeScript source). @@ -335,6 +349,7 @@ shape they accept. See [Source vs Artifact](#source-vs-artifact) below. | Command | Description | |---------|-------------| | `os compile [config]` | Compile configuration to a JSON artifact (`dist/objectstack.json`) | +| `os build [config]` | Alias for `os compile` (scaffolded projects wire it as `npm run build`) | | `os validate [config]` | Validate schema, CEL predicates, and widget bindings — the same gates as `os compile`/`os build`, no artifact emitted | | `os info [config]` | Display metadata summary (objects, fields, apps, agents, etc.) | @@ -358,17 +373,24 @@ os compile --json # JSON output for CI pipelines ──────────────────────────────────────── → Loading configuration... Config: objectstack.config.ts - Load time: 104ms + Load time: 57ms + → Normalizing stack definition... + → Lowering inline handlers... → Validating protocol compliance... + → Validating expressions (ADR-0032)... + → Checking dashboard widget bindings (ADR-0021)... + → Checking SDUI styling (ADR-0065)... + → Checking security posture (ADR-0090 D7)... + → Collecting package docs (ADR-0046)... → Writing artifact... - ✓ Build complete (312ms) + ✓ Build complete (74ms) - Data: 10 Objects 217 Fields - UI: 1 Apps 3 Dashboards 8 Reports 10 Actions - Logic: 5 Flows 5 Agents 2 APIs + Data: 2 Objects 6 Fields + UI: 1 Views 1 Actions + Runtime: 3 plugins - Artifact: dist/objectstack.json (48.2 KB) + Artifact: dist/objectstack.json (7.6 KB) ``` The resulting `dist/objectstack.json` is a **portable, self-describing deployment unit** — @@ -434,21 +456,21 @@ os info --json # JSON output for tooling ◆ Info ──────────────────────────────────────── - Enterprise CRM v3.0.0 - com.example.crm - Namespace: crm - Type: app + My App v0.1.0 + my-app + Minimal ObjectStack environment — a clean slate for building. + Namespace: my_app + Type: app - Data: 10 Objects 217 Fields - UI: 1 Apps 3 Dashboards 8 Reports 10 Actions - Logic: 5 Flows 5 Agents 2 APIs + Data: 2 Objects 6 Fields + UI: 1 Views 1 Actions + Runtime: 3 plugins Objects: - account (16 fields, user) — Account - contact (24 fields, user) — Contact - ... + my_app_note (2 fields, user) — Note + my_app_ticket (4 fields, user) — Ticket - Loaded in 90ms + Loaded in 59ms ``` ### Schema migrations @@ -552,9 +574,21 @@ os create example my-app # Create examples/my-app | Command | Description | |---------|-------------| +| `os lint [config]` | Check metadata for style and convention issues (beyond `validate`'s hard gates) | | `os test [files]` | Run Quality Protocol test scenarios against a running server | | `os doctor` | Check development environment health | +#### `os lint` + +Style and convention checks on top of `os validate` — naming, labels, translation coverage — with a 0-100 quality score: + +```bash +os lint # Style / convention checks +os lint --score # Append a 0-100 metadata quality score (letter-graded) +os lint --fix # Show what would be fixed (dry-run) +os lint --json # JSON output for CI +``` + #### `os test` Runs Quality Protocol test scenarios (JSON-based BDD) against a running ObjectStack server. diff --git a/content/docs/getting-started/common-patterns.mdx b/content/docs/getting-started/common-patterns.mdx index fe8cefe8e0..91f082b466 100644 --- a/content/docs/getting-started/common-patterns.mdx +++ b/content/docs/getting-started/common-patterns.mdx @@ -1,6 +1,6 @@ --- title: Common Patterns -description: Top 10 patterns for building applications with ObjectStack — CRUD, search, auth, realtime, and more +description: Top 10 patterns for building applications with ObjectStack — CRUD, views, flows, agents, security, and more --- # Common Patterns Guide @@ -8,7 +8,7 @@ description: Top 10 patterns for building applications with ObjectStack — CRUD This guide covers the most common patterns you will use when building applications with ObjectStack. Each pattern includes a complete, copy-pasteable example. -**Import:** `import { defineStack, defineView, defineApp, defineFlow, defineAgent } from '@objectstack/spec'` +**Import:** `import { defineStack, defineView, defineApp, defineFlow, defineAgent, definePermissionSet } from '@objectstack/spec'` @@ -46,7 +46,8 @@ export default defineStack({ { label: 'Complete', value: 'complete' }, { label: 'Archived', value: 'archived' } ]}, - owner: { label: 'Owner', type: 'lookup', reference: 'user' }, + // Person references target the platform user object `sys_user` + owner: { label: 'Owner', type: 'lookup', reference: 'sys_user' }, due_date: { label: 'Due Date', type: 'date' }, budget: { label: 'Budget', type: 'currency', currencyConfig: { precision: 2, defaultCurrency: 'USD' } }, } @@ -73,7 +74,8 @@ export default defineStack({ { label: 'Complete', value: 'complete' }, { label: 'Archived', value: 'archived' } ]}, - owner: { label: 'Owner', type: 'lookup', reference: 'user' }, + // Person references target the platform user object `sys_user` + owner: { label: 'Owner', type: 'lookup', reference: 'sys_user' }, due_date: { label: 'Due Date', type: 'date' }, budget: { label: 'Budget', type: 'currency', currencyConfig: { precision: 2, defaultCurrency: 'USD' } }, }, @@ -134,7 +136,8 @@ Create a parent-child relationship between a master and its detail records. Note product: { label: 'Product', type: 'lookup', reference: 'product' }, quantity: { label: 'Qty', type: 'number', min: 1, required: true }, unit_price: { label: 'Unit Price', type: 'currency' }, - line_total: { label: 'Line Total', type: 'formula', expression: 'quantity * unit_price' }, + // Formula expressions are CEL — reference fields through `record.` + line_total: { label: 'Line Total', type: 'formula', expression: 'record.quantity * record.unit_price', returnType: 'number' }, } } ] @@ -236,7 +239,10 @@ export const crm = defineApp({ ## 6. Automated Flow (Record-Triggered) -Create an automation that fires when a record is created or updated. +Create an automation that fires when a record changes. The **start node's `config` +binds the trigger**: `objectName` plus one lifecycle event in `triggerType` +(`record-after-create`, `record-after-update`, `record-after-delete`, or the +`record-before-*` forms) — without it the flow never fires. ```typescript import { defineFlow } from '@objectstack/spec'; @@ -245,15 +251,23 @@ export const assignmentNotification = defineFlow({ name: 'task_assignment_notification', label: 'Task Assignment Notification', type: 'record_change', + status: 'active', // arm the trigger deliberately (default 'draft' is ambiguous) nodes: [ - { id: 'start', type: 'start', label: 'Start' }, + { + id: 'start', + type: 'start', + label: 'Start', + // Trigger binding: which object + which lifecycle event launches this flow + config: { objectName: 'task', triggerType: 'record-after-update' } + }, { id: 'notify', type: 'notify', label: 'Notify Assignee', config: { - // String fields use single-brace {…} templates, not {{…}} - to: '{record.assigned_to.email}', + // String fields use single-brace {…} templates, not {{…}}. + // `to` takes recipient USER IDs — a lookup value interpolates to the id. + to: '{record.assigned_to}', subject: 'Task Assigned: {record.title}', body: 'You have been assigned to task "{record.title}".' } @@ -289,8 +303,8 @@ Define a multi-step approval flow for records. { label: 'Approved', value: 'approved' }, { label: 'Rejected', value: 'rejected' } ]}, - submitted_by: { label: 'Submitted By', type: 'lookup', reference: 'user' }, - approved_by: { label: 'Approved By', type: 'lookup', reference: 'user' }, + submitted_by: { label: 'Submitted By', type: 'lookup', reference: 'sys_user' }, + approved_by: { label: 'Approved By', type: 'lookup', reference: 'sys_user' }, }, enable: { trackHistory: true, @@ -301,11 +315,40 @@ Define a multi-step approval flow for records. name: 'expense_approval', label: 'Expense Approval', type: 'record_change', + status: 'active', nodes: [ - { id: 'start', type: 'start', label: 'Start' }, + { + id: 'start', + type: 'start', + label: 'Start', + config: { + objectName: 'expense_report', + triggerType: 'record-after-update', + // Start condition (bare CEL): launch only on the draft → submitted transition + condition: "record.status == 'submitted' && previous.status != 'submitted'" + } + }, { id: 'check_amount', type: 'decision', label: 'Check Amount' }, - { id: 'auto_approve', type: 'update_record', label: 'Auto Approve' }, - { id: 'request_approval', type: 'notify', label: 'Request Approval' }, + { + id: 'auto_approve', + type: 'update_record', + label: 'Auto Approve', + config: { + objectName: 'expense_report', + filter: { id: '{record.id}' }, + fields: { status: 'approved' } + } + }, + { + id: 'request_approval', + type: 'notify', + label: 'Request Approval', + config: { + to: '{record.submitted_by}', + subject: 'Expense approval needed: {record.title}', + body: 'Expense "{record.title}" needs manual approval.' + } + }, { id: 'end', type: 'end', label: 'End' } ], edges: [ @@ -388,10 +431,15 @@ const page2 = { ...page1, offset: 25 }; const page3 = { ...page1, offset: 50 }; ``` -### Cursor-Based (Scalable) +### Keyset (Scalable) + +Instead of skipping N rows, filter past the last row you already received — +stable under concurrent writes and cheap at any depth. (The query schema also +reserves a `cursor` property, but it is not executed by the engine — use the +`where` + `orderBy` + `limit` form below.) ```typescript -// First page — no cursor yet +// First page const firstPage = { object: 'activity', fields: ['id', 'type', 'description', 'created_at'], @@ -399,10 +447,11 @@ const firstPage = { limit: 50 }; -// Next page — pass the opaque cursor token returned with the previous response +// Next page — filter past the last row of the previous page +const lastRow = previousPage[previousPage.length - 1]; const nextPage = { ...firstPage, - cursor: { /* opaque cursor token from the previous response */ } + where: { created_at: { $lt: lastRow.created_at } } }; ``` diff --git a/content/docs/getting-started/quick-reference.mdx b/content/docs/getting-started/quick-reference.mdx index b47fc9fc5b..5b1ae711a2 100644 --- a/content/docs/getting-started/quick-reference.mdx +++ b/content/docs/getting-started/quick-reference.mdx @@ -12,7 +12,7 @@ Fast lookup for the ObjectStack protocols organized by category. Click on any protocol name to view its complete API reference. -## Data Protocol (18 schemas) +## Data Protocol (17 schemas) Core business logic and data modeling schemas. @@ -20,24 +20,23 @@ Core business logic and data modeling schemas. |:---------|:-----------|:------------|:--------| | **[Field](/docs/references/data/field)** | `field.zod.ts` | Field, FieldType, SelectOption | Field types for data modeling | | **[Object](/docs/references/data/object)** | `object.zod.ts` | Object, ObjectCapabilities | Object/table definitions | -| **[Query](/docs/references/data/query)** | `query.zod.ts` | Query, QueryOptions | Query AST with joins, aggregations | +| **[Query](/docs/references/data/query)** | `query.zod.ts` | Query, QueryAST | Query AST with joins, aggregations | | **[Filter](/docs/references/data/filter)** | `filter.zod.ts` | QueryFilter, FilterCondition | Advanced filtering operators | | **[Validation](/docs/references/data/validation)** | `validation.zod.ts` | ValidationRule | Business validation rules | -| **[Dataset](/docs/references/ui/dataset)** | `dataset.zod.ts` | Dataset, DatasetMode | Reusable dataset definitions | -| **[Datasource](/docs/references/data/datasource)** | `datasource.zod.ts` | Datasource, DatasourceType | Database connection configs | +| **[Datasource](/docs/references/data/datasource)** | `datasource.zod.ts` | Datasource, DatasourceCapabilities | Database connection configs | | **[Analytics](/docs/references/data/analytics)** | `analytics.zod.ts` | Analytics | Data analytics and aggregation | | **[Mapping](/docs/references/data/mapping)** | `mapping.zod.ts` | FieldMapping | Field transformation mappings | | **[Hook](/docs/references/data/hook)** | `hook.zod.ts` | Hook, HookEvent | Lifecycle event hooks | | **[Data Engine](/docs/references/data/data-engine)** | `data-engine.zod.ts` | DataEngine | Data engine configuration | | **[Driver](/docs/references/data/driver)** | `driver.zod.ts` | Driver, DriverCapabilities | Database driver interface | -| **[SQL Driver](/docs/references/data/driver-sql)** | `driver-sql.zod.ts` | SQLDriver | SQL-specific driver | -| **[NoSQL Driver](/docs/references/data/driver-nosql)** | `driver-nosql.zod.ts` | NoSQLDriver | NoSQL-specific driver | +| **[SQL Driver](/docs/references/data/driver-sql)** | `driver-sql.zod.ts` | SQLDriverConfig, SQLDialect | SQL-specific driver | +| **[NoSQL Driver](/docs/references/data/driver-nosql)** | `driver-nosql.zod.ts` | NoSQLDriverConfig | NoSQL-specific driver | | **[Document](/docs/references/data/document)** | `document.zod.ts` | Document | Document-oriented data | | **[External Lookup](/docs/references/data/external-lookup)** | `external-lookup.zod.ts` | ExternalLookup | External data lookups | | **Postgres Driver** | `driver/postgres.zod.ts` | PostgresConfig | PostgreSQL configuration | | **Mongo Driver** | `driver/mongo.zod.ts` | MongoConfig | MongoDB configuration | -## UI Protocol (10 schemas) +## UI Protocol (11 schemas) Presentation layer - views, forms, dashboards, and themes. @@ -47,6 +46,7 @@ Presentation layer - views, forms, dashboards, and themes. | **[Page](/docs/references/ui/page)** | `page.zod.ts` | Page, PageComponent | FlexiPage layouts | | **[App](/docs/references/ui/app)** | `app.zod.ts` | App, NavigationItem | Application navigation structure | | **[Dashboard](/docs/references/ui/dashboard)** | `dashboard.zod.ts` | Dashboard, DashboardWidget | Dashboard layouts and widgets | +| **[Dataset](/docs/references/ui/dataset)** | `dataset.zod.ts` | Dataset, DatasetDimension, DatasetMeasure | Semantic-layer datasets for dashboards (ADR-0021) | | **[Report](/docs/references/ui/report)** | `report.zod.ts` | Report, ReportType | Report definitions | | **[Action](/docs/references/ui/action)** | `action.zod.ts` | Action, ActionType | UI button actions | | **[Component](/docs/references/ui/component)** | `component.zod.ts` | PageComponent variants | Reusable UI components | @@ -62,60 +62,65 @@ Plugin architecture, manifests, and kernel runtime. |:---------|:-----------|:------------|:--------| | **[Manifest](/docs/references/kernel/manifest)** | `manifest.zod.ts` | Manifest | Package manifest (objectstack.config.ts) | | **[Context](/docs/references/kernel/context)** | `context.zod.ts` | KernelContext | Runtime execution context | -| **[Plugin](/docs/references/kernel/plugin)** | `plugin.zod.ts` | Plugin, PluginHook | Plugin system interface | +| **[Plugin](/docs/references/kernel/plugin)** | `plugin.zod.ts` | Plugin, PluginLifecycle | Plugin system interface | | **[Plugin Capability](/docs/references/kernel/plugin-capability)** | `plugin-capability.zod.ts` | PluginCapability | Plugin capability declarations | -| **[Plugin Lifecycle](/docs/references/kernel/plugin-lifecycle-events)** | `plugin-lifecycle-events.zod.ts` | PluginLifecycleEvents | Plugin lifecycle events | -| **[Plugin Lifecycle Advanced](/docs/references/kernel/plugin-lifecycle-advanced)** | `plugin-lifecycle-advanced.zod.ts` | PluginLifecycleAdvanced | Advanced lifecycle hooks | -| **[Plugin Loading](/docs/references/kernel/plugin-loading)** | `plugin-loading.zod.ts` | PluginLoading | Plugin loading and init | -| **[Plugin Security](/docs/references/kernel/plugin-security-advanced)** | `plugin-security-advanced.zod.ts` | PluginSecurityAdvanced | Plugin sandboxing | -| **[Plugin Structure](/docs/references/kernel/plugin-structure)** | `plugin-structure.zod.ts` | PluginStructure | Plugin file conventions | -| **[Plugin Validator](/docs/references/kernel/plugin-validator)** | `plugin-validator.zod.ts` | PluginValidator | Plugin validation | -| **[Plugin Versioning](/docs/references/kernel/plugin-versioning)** | `plugin-versioning.zod.ts` | PluginVersioning | Version compatibility | -| **[Service Registry](/docs/references/kernel/service-registry)** | `service-registry.zod.ts` | ServiceRegistry | Service discovery | -| **[Startup Orchestrator](/docs/references/kernel/startup-orchestrator)** | `startup-orchestrator.zod.ts` | StartupOrchestrator | System startup | -| **[Events](/docs/kernel/events)** | `events.zod.ts` | Event, EventBus | System event bus | +| **[Plugin Lifecycle](/docs/references/kernel/plugin-lifecycle-events)** | `plugin-lifecycle-events.zod.ts` | PluginEventBase, EventPhase | Plugin lifecycle events | +| **[Plugin Lifecycle Advanced](/docs/references/kernel/plugin-lifecycle-advanced)** | `plugin-lifecycle-advanced.zod.ts` | AdvancedPluginLifecycleConfig, PluginHealthCheck | Advanced lifecycle hooks | +| **[Plugin Loading](/docs/references/kernel/plugin-loading)** | `plugin-loading.zod.ts` | PluginLoadingConfig | Plugin loading and init | +| **[Plugin Security](/docs/references/kernel/plugin-security-advanced)** | `plugin-security-advanced.zod.ts` | KernelSecurityPolicy, PluginPermission | Plugin sandboxing | +| **[Plugin Structure](/docs/references/kernel/plugin-structure)** | `plugin-structure.zod.ts` | OpsPluginStructure | Plugin file conventions | +| **[Plugin Validator](/docs/references/kernel/plugin-validator)** | `plugin-validator.zod.ts` | ValidationResult, PluginMetadata | Plugin validation | +| **[Plugin Versioning](/docs/references/kernel/plugin-versioning)** | `plugin-versioning.zod.ts` | PluginCompatibilityMatrix, DeprecationNotice | Version compatibility | +| **[Service Registry](/docs/references/kernel/service-registry)** | `service-registry.zod.ts` | ServiceRegistryConfig, ServiceMetadata | Service discovery | +| **[Startup Orchestrator](/docs/references/kernel/startup-orchestrator)** | `startup-orchestrator.zod.ts` | StartupOptions, StartupOrchestrationResult | System startup | +| **[Events](/docs/kernel/events)** | `events.zod.ts` | Event, EventBusConfig | System event bus | | **[Feature](/docs/references/kernel/feature)** | `feature.zod.ts` | FeatureFlag | Feature flags | -| **[Metadata Loader](/docs/references/kernel/metadata-loader)** | `metadata-loader.zod.ts` | MetadataLoader | Metadata loading | -| **[Package Registry](/docs/references/kernel/package-registry)** | `package-registry.zod.ts` | PackageRegistry | Package resolution | +| **[Metadata Loader](/docs/references/kernel/metadata-loader)** | `metadata-loader.zod.ts` | MetadataLoaderContract | Metadata loading | +| **[Package Registry](/docs/references/kernel/package-registry)** | `package-registry.zod.ts` | InstalledPackage, InstallPackageRequest | Package resolution | -## System Protocol (22 schemas) +## System Protocol (19 schemas) Runtime environment, logging, jobs, caching, and observability. | Protocol | Source File | Key Schemas | Purpose | |:---------|:-----------|:------------|:--------| -| **[Audit](/docs/references/system/audit)** | `audit.zod.ts` | AuditLog, AuditEvent | Audit trail logging | +| **[Audit](/docs/references/system/audit)** | `audit.zod.ts` | AuditEvent, AuditConfig | Audit trail logging | | **[Auth Config](/docs/references/system/auth-config)** | `auth-config.zod.ts` | AuthConfig | Authentication configuration | | **[Cache](/docs/references/system/cache)** | `cache.zod.ts` | CacheConfig | Caching layer | -| **[Change Management](/docs/references/system/change-management)** | `change-management.zod.ts` | ChangeManagement | Change tracking | +| **[Change Management](/docs/references/system/change-management)** | `change-management.zod.ts` | ChangeRequest, RollbackPlan | Change tracking | | **[Collaboration](/docs/references/system/collaboration)** | `collaboration.zod.ts` | Collaboration | Real-time collab | -| **Compliance** | `compliance.zod.ts` | Compliance | Regulatory controls | | **[Encryption](/docs/references/system/encryption)** | `encryption.zod.ts` | Encryption | Encryption & keys | -| **[HTTP Server](/docs/references/system/http-server)** | `http-server.zod.ts` | HTTPServer | HTTP server config | +| **[HTTP Server](/docs/references/system/http-server)** | `http-server.zod.ts` | HttpServerConfig, MiddlewareConfig | HTTP server config | | **[Job](/docs/references/system/job)** | `job.zod.ts` | Job, JobSchedule | Background job queue | | **[Logging](/docs/references/system/logging)** | `logging.zod.ts` | LoggingConfig | Structured logging | -| **Masking** | `masking.zod.ts` | Masking | Data masking | -| **[Message Queue](/docs/references/system/message-queue)** | `message-queue.zod.ts` | MessageQueue | Message queuing | -| **[Metadata Persistence](/docs/references/system/metadata-persistence)** | `metadata-persistence.zod.ts` | MetadataPersistence | Metadata storage | +| **[Message Queue](/docs/references/system/message-queue)** | `message-queue.zod.ts` | MessageQueueConfig, TopicConfig | Message queuing | +| **[Metadata Persistence](/docs/references/system/metadata-persistence)** | `metadata-persistence.zod.ts` | MetadataHistoryRecord, MetadataDiffResult | Metadata storage | | **[Metrics](/docs/references/system/metrics)** | `metrics.zod.ts` | Metrics | Application metrics | | **[Migration](/docs/references/system/migration)** | `migration.zod.ts` | Migration | Schema migration | | **[Notification](/docs/references/system/notification)** | `notification.zod.ts` | Notification | Notifications | -| **[Object Storage](/docs/references/system/object-storage)** | `object-storage.zod.ts` | ObjectStorage | Object storage | -| **[Search Engine](/docs/references/system/search-engine)** | `search-engine.zod.ts` | SearchEngine | Full-text search | -| **[Service Registry](/docs/references/kernel/service-registry)** | `service-registry.zod.ts` | ServiceRegistry | System services | +| **[Object Storage](/docs/references/system/object-storage)** | `object-storage.zod.ts` | BucketConfig, ObjectMetadata | Object storage | +| **[Search Engine](/docs/references/system/search-engine)** | `search-engine.zod.ts` | SearchConfig, SearchIndexConfig | Full-text search | | **[Tracing](/docs/references/system/tracing)** | `tracing.zod.ts` | Tracing | Distributed tracing | | **[Translation](/docs/references/system/translation)** | `translation.zod.ts` | Translation | i18n support | | **[Worker](/docs/references/system/worker)** | `worker.zod.ts` | Worker | Background workers | -## AI Protocol (3 schemas) +## AI Protocol (11 schemas) -AI/ML capabilities - agents, models, RAG, and cost tracking. +AI/ML capabilities - agents, skills, tools, MCP exposure, RAG, and cost tracking. | Protocol | Source File | Key Schemas | Purpose | |:---------|:-----------|:------------|:--------| | **[Agent](/docs/references/ai/agent)** | `agent.zod.ts` | Agent, AITool | AI agent definitions | +| **[Skill](/docs/references/ai/skill)** | `skill.zod.ts` | Skill, SkillTriggerCondition | Reusable agent skills | +| **[Tool](/docs/references/ai/tool)** | `tool.zod.ts` | Tool, ToolCategory | Agent tool definitions | +| **[MCP](/docs/references/ai/mcp)** | `mcp.zod.ts` | MCPServerRef, MCPToolBinding | MCP server exposure and tool bindings | | **[Model Registry](/docs/references/ai/model-registry)** | `model-registry.zod.ts` | ModelRegistry, ModelProvider | LLM model management | | **[Conversation](/docs/references/ai/conversation)** | `conversation.zod.ts` | ConversationSession | Conversation management | +| **[Embedding](/docs/references/ai/embedding)** | `embedding.zod.ts` | EmbeddingModel, VectorStore | Embedding models and vector stores | +| **[Knowledge Source](/docs/references/ai/knowledge-source)** | `knowledge-source.zod.ts` | KnowledgeSource, KnowledgeRefreshPolicy | RAG ingestion sources | +| **[Knowledge Document](/docs/references/ai/knowledge-document)** | `knowledge-document.zod.ts` | KnowledgeDocument, KnowledgeChunk | RAG documents and chunks | +| **[Usage](/docs/references/ai/usage)** | `usage.zod.ts` | AIUsageRecord, TokenUsage | AI usage and cost tracking | +| **[Solution Blueprint](/docs/references/ai/solution-blueprint)** | `solution-blueprint.zod.ts` | BlueprintObject, BlueprintApp | Blueprint format for AI app generation | ## API Protocol (19 schemas) @@ -123,20 +128,20 @@ REST/GraphQL endpoints, real-time subscriptions, and discovery. | Protocol | Source File | Key Schemas | Purpose | |:---------|:-----------|:------------|:--------| -| **[Contract](/docs/references/api/contract)** | `contract.zod.ts` | APIContract | API contract definitions | -| **[Endpoint](/docs/references/api/endpoint)** | `endpoint.zod.ts` | Endpoint, EndpointMethod | REST endpoint configuration | +| **[Contract](/docs/references/api/contract)** | `contract.zod.ts` | BaseResponse, BulkRequest | API contract definitions | +| **[Endpoint](/docs/references/api/endpoint)** | `endpoint.zod.ts` | ApiEndpoint, ApiMapping | REST endpoint configuration | | **[Router](/docs/references/api/router)** | `router.zod.ts` | Router, Route | API routing rules | | **[OData](/docs/references/api/odata)** | `odata.zod.ts` | ODataQuery | OData protocol support | -| **[GraphQL](/docs/references/api/graphql)** | `graphql.zod.ts` | GraphQLSchema | GraphQL API config | -| **[Realtime](/docs/references/api/realtime)** | `realtime.zod.ts` | Subscription, Channel | WebSocket subscriptions | +| **[GraphQL](/docs/references/api/graphql)** | `graphql.zod.ts` | GraphQLConfig, FederationEntity | GraphQL API config | +| **[Realtime](/docs/references/api/realtime)** | `realtime.zod.ts` | Subscription, RealtimeEvent | WebSocket subscriptions | | **[WebSocket](/docs/references/api/websocket)** | `websocket.zod.ts` | WebSocketConfig | WebSocket protocol | -| **[Discovery](/docs/references/api/discovery)** | `discovery.zod.ts` | ServiceDiscovery | API discovery and metadata | -| **[Batch](/docs/references/api/batch)** | `batch.zod.ts` | BatchRequest | Batch API processing | -| **[HTTP Cache](/docs/references/api/http-cache)** | `http-cache.zod.ts` | CacheStrategy | HTTP caching | +| **[Discovery](/docs/references/api/discovery)** | `discovery.zod.ts` | Discovery, ServiceInfo | API discovery and metadata | +| **[Batch](/docs/references/api/batch)** | `batch.zod.ts` | BatchConfig, CrossObjectBatchRequest | Batch API processing | +| **[HTTP Cache](/docs/references/api/http-cache)** | `http-cache.zod.ts` | CacheControl, ETag | HTTP caching | | **[Errors](/docs/references/api/errors)** | `errors.zod.ts` | ErrorResponse | Error responses | -| **[Protocol](/docs/references/api/protocol)** | `protocol.zod.ts` | ProtocolDefinition | Protocol definitions | -| **[REST Server](/docs/references/api/rest-server)** | `rest-server.zod.ts` | RESTServer | REST server config | -| **[Auth](/docs/references/api/auth)** | `auth.zod.ts` | Auth | API authentication | +| **[Protocol](/docs/references/api/protocol)** | `protocol.zod.ts` | BatchDataRequest, CheckPermissionRequest | Console/client request-response contracts | +| **[REST Server](/docs/references/api/rest-server)** | `rest-server.zod.ts` | RestApiConfig, CrudEndpointsConfig | REST server config | +| **[Auth](/docs/references/api/auth)** | `auth.zod.ts` | LoginRequest, Session | API authentication | | **[Analytics](/docs/references/api/analytics)** | `analytics.zod.ts` | Analytics | API usage analytics | | **[Documentation](/docs/references/api/documentation)** | `documentation.zod.ts` | Documentation | API docs generation | | **[Metadata](/docs/references/api/metadata)** | `metadata.zod.ts` | Metadata | API metadata endpoints | @@ -152,10 +157,10 @@ Flows, state machines, approvals, and integrations. | **[Flow](/docs/references/automation/flow)** | `flow.zod.ts` | Flow, FlowNode | Visual workflow builder | | **[Approval](/docs/references/automation/approval)** | `approval.zod.ts` | ApprovalNodeConfig | Flow approval-node config | | **[State Machine](/docs/references/automation/state-machine)** | `state-machine.zod.ts` | StateMachine | State machine definitions | -| **[Webhook](/docs/references/automation/webhook)** | `webhook.zod.ts` | Webhook, WebhookEvent | Outbound webhooks | +| **[Webhook](/docs/references/automation/webhook)** | `webhook.zod.ts` | Webhook, WebhookReceiver | Outbound webhooks | | **[ETL](/docs/references/automation/etl)** | `etl.zod.ts` | ETLPipeline | Data transformation pipelines | | **[Trigger Registry](/docs/references/automation/trigger-registry)** | `trigger-registry.zod.ts` | TriggerRegistry | Event-driven triggers | -| **[Sync](/docs/references/automation/sync)** | `sync.zod.ts` | SyncConfig | Bi-directional data sync | +| **[Sync](/docs/references/automation/sync)** | `sync.zod.ts` | DataSyncConfig, SyncMode | Bi-directional data sync | ## Security Protocol (4 schemas) @@ -179,15 +184,16 @@ User identity, organizations, and position management. | **[Position](/docs/references/identity/position)** | `position.zod.ts` | Position | Permission-set distribution (岗位, ADR-0090) | | **[SCIM](/docs/references/identity/scim)** | `scim.zod.ts` | SCIMUser, SCIMGroup | SCIM 2.0 provisioning | -## Cloud Protocol (4 schemas) +## Cloud Protocol (5 schemas) -Marketplace, licensing, and multi-tenancy. +Environments, marketplace, licensing, and multi-tenancy. | Protocol | Source File | Key Schemas | Purpose | |:---------|:-----------|:------------|:--------| -| **[Marketplace](/docs/references/cloud/marketplace)** | `marketplace.zod.ts` | Package, PackageVersion | Plugin marketplace | -| **[Plugin Registry](/docs/references/kernel/plugin-registry)** | `plugin-registry.zod.ts` | PluginRegistry | Plugin versioning | -| **[Plugin Security](/docs/references/cloud/plugin-security)** | `plugin-security.zod.ts` | PluginSecurity | Plugin security policies | +| **[Environment](/docs/references/cloud/environment)** | `environment.zod.ts` | Environment, EnvironmentType | Deployment environments | +| **[Marketplace](/docs/references/cloud/marketplace)** | `marketplace.zod.ts` | MarketplaceListing, PackageSubmission | Plugin marketplace | +| **[Plugin Registry](/docs/references/kernel/plugin-registry)** | `plugin-registry.zod.ts` | PluginRegistryEntry, PluginVendor | Plugin registry entries and quality metrics | +| **[Plugin Security](/docs/references/cloud/plugin-security)** | `plugin-security.zod.ts` | PluginSecurityProtocol, SBOM | Plugin security policies | | **[Tenant](/docs/references/cloud/tenant)** | `tenant.zod.ts` | Tenant | Multi-tenancy isolation | ## Integration Protocol (7 schemas) @@ -204,16 +210,17 @@ External system connectors and adapters. | **[GitHub](/docs/references/integration/connector)** | `connector/github.zod.ts` | GitHubConnector | GitHub API integration | | **[Vercel](/docs/references/integration/connector)** | `connector/vercel.zod.ts` | VercelConnector | Vercel deployment | -## Shared Protocol (4 schemas) +## Shared Protocol (5 schemas) Common utilities used across all protocols. | Protocol | Source File | Key Schemas | Purpose | |:---------|:-----------|:------------|:--------| -| **[HTTP](/docs/references/shared/http)** | `http.zod.ts` | HTTPRequest, HTTPResponse | HTTP utilities | -| **[Identifiers](/docs/references/shared/identifiers)** | `identifiers.zod.ts` | ID formats | Standard ID patterns | -| **Mapping** | `mapping.zod.ts` | MappingRule | Field mapping utilities | -| **Connector Auth** | `connector-auth.zod.ts` | ConnectorAuth | Connector auth patterns | +| **[Expression](/docs/references/shared/expression)** | `expression.zod.ts` | Expression, ExpressionInput | CEL expression values and inputs | +| **[HTTP](/docs/references/shared/http)** | `http.zod.ts` | HttpRequest, HttpMethod, CorsConfig | HTTP utilities | +| **[Identifiers](/docs/references/shared/identifiers)** | `identifiers.zod.ts` | SystemIdentifier, SnakeCaseIdentifier | Standard ID patterns | +| **[Mapping](/docs/references/shared/mapping)** | `mapping.zod.ts` | FieldMapping, TransformType | Field mapping utilities | +| **[Connector Auth](/docs/references/shared/connector-auth)** | `connector-auth.zod.ts` | ConnectorAuthConfig | Connector auth patterns | ## QA Protocol (1 schema) diff --git a/content/docs/getting-started/validating-metadata.mdx b/content/docs/getting-started/validating-metadata.mdx index a2fe8fbca9..aa7b8e26de 100644 --- a/content/docs/getting-started/validating-metadata.mdx +++ b/content/docs/getting-started/validating-metadata.mdx @@ -62,7 +62,44 @@ dangling one. So `os validate` is the fast inner-loop check (no artifact); `os build` is what you run when you need the deployable artifact. A config that passes `os validate` -will not fail `os build` on schema/predicate/binding grounds. +will not fail `os build` on schema/predicate/binding grounds. Both entry points +also check SDUI styling (ADR-0065), and `os validate` additionally runs a set of +view- and page-shape checks — list-view navigation modes (ADR-0053), view +container shape, and JSX/React page sources (ADR-0080/0081) — that catch UI +metadata which would otherwise be silently dropped. + +A clean run walks each gate and reports timing: + +``` +◆ Validate +──────────────────────────────────────── + → Loading configuration... + Config: /path/to/support-desk/objectstack.config.ts + Load time: 21ms + → Validating against ObjectStack Protocol... + → Validating expressions (ADR-0032)... + → Checking list-view navigation modes (ADR-0053)... + → Checking view container shape... + → Checking dashboard widget bindings (ADR-0021)... + → Checking SDUI styling (ADR-0065)... + → Checking JSX-source pages (ADR-0080)... + → Checking React-source pages (ADR-0081)... + → Checking React-source page props (ADR-0081)... + → Checking source-page styling (ADR-0065)... + → Checking capability references (ADR-0066)... + → Checking flow trigger wiring... + → Checking security posture (ADR-0090 D7)... + + ✓ Validation passed (64ms) + + Support Desk v0.1.0 + Data: 1 Objects 6 Fields + UI: 1 Apps 1 Views 1 Actions +``` + +On failure the exit code is non-zero and the error is located and corrective — +see [the gate in action](/docs/getting-started/build-with-claude-code#4-the-gate-os-validate-catches-ai-mistakes) +for the bare-reference example verbatim. `os lint` is a **separate** pass — style and convention checks (snake_case @@ -79,8 +116,7 @@ npm run typecheck # tsc --noEmit — types against @objectstack/spec ``` **Rule of thumb: never report a metadata change as done until `npm run validate` -passes.** In the example apps the equivalent is `pnpm --filter validate` -(`pnpm verify` in `app-showcase`, which chains validate + typecheck + test). +passes.** ### Checking a single expression diff --git a/content/docs/getting-started/your-first-project.mdx b/content/docs/getting-started/your-first-project.mdx index 6008a4a9e0..79a9cc8d31 100644 --- a/content/docs/getting-started/your-first-project.mdx +++ b/content/docs/getting-started/your-first-project.mdx @@ -240,6 +240,10 @@ npm run validate bindings — the classes of mistakes that otherwise fail *silently* at runtime. See [Validating Metadata](/docs/getting-started/validating-metadata). +Beyond pass/fail, `npx os lint --score` grades the data model 0–100 against the +platform's design conventions (relationship patterns, missing select options, +roll-ups, name fields) — a quick health check as the model grows. + ## 6. Build the deployable artifact ```bash @@ -258,6 +262,26 @@ Database URL, auth secret, and environment identity stay *outside* the artifact and are injected at boot via flags or `OS_*` environment variables — which is exactly what makes it container-friendly. +## 7. Hand the project to your agent + +You just did by hand what an agent does in seconds — and the scaffolder already +prepared the project for that handoff: the **skills bundle** is installed (step +1.3) and `AGENTS.md` routes each task to the right skill and enforces the +validate gate. Open the project in Claude Code (or Cursor / Copilot) and +describe the next feature instead of typing it: + +> Add a `status` filter to the note list view, a dashboard with a metric of +> published notes, and a daily flow that archives notes older than 90 days. +> Run `npm run validate` when done. + +Each of those lands in a different metadata domain (UI, dashboards, +automation) — the agent loads the matching skill per task. The full loop, and a +per-domain prompt catalog, is on +[Build with Claude Code](/docs/getting-started/build-with-claude-code#beyond-the-first-app--one-skill-per-metadata-domain). +If you upgrade `@objectstack/spec` later, re-run +`npx skills add objectstack-ai/framework/skills --all` to keep the agent in +sync with the schemas you run. + ## Next steps