standards
Schema sourcesConcepts

Schema sources

Understand who owns each object on a workspace, what a sync may change, and how a source lives and dies over the API.


A schema source is the slice of a workspace's schema that one codebase owns. You declare objects and views with the builders, give the set an id, and push it with schema.sync. The workspace applies it as system definitions. Users keep working on top: their attributes and views survive every sync.

import { createStandards, defineSchemaSource, object, text } from "@stndrds/client";

const service = object({ name: "services", label: "Service" })
  .labelExpression("{{ name }}")
  .attribute(text({ name: "name", label: "Name" }).required());

export const statusSource = defineSchemaSource("status-page", {
  label: "Status page",
  objects: [service],
});

const standards = createStandards({
  baseUrl: "https://api.standards.new/v1",
  apiKey: process.env.STANDARDS_API_KEY!,
});

const result = await standards.schema.sync(statusSource);
// { applied: true, hash, objects: { created, updated, deleted }, views: { created, updated, adopted, released } }

Three tiers

Every object on a workspace belongs to one of three tiers: native, system or runtime.

  • Native objects ship with the platform: skill, memory, drives, artifact, meeting. Their names are reserved. Declaring one in code fails at build time with ReservedObjectNameError, and the API refuses them too.
  • System objects and attributes are declared in code and pushed by schema.sync. They carry system: true and are immutable in the app.
  • Runtime objects and attributes are created by users in the app, or over POST /schema/objects and its attribute routes under architect:create. They carry system: false.
SystemRuntime
Who createsYour code, through schema.sync.Users in the app, or an API call under architect:create.
Who changesYour code, on the next sync. Users cannot edit, retype or delete it.Users in the app.
Who deletesYour code. An attribute removed from the source is dropped with its values; an object removed from the source is demoted, not deleted.Users in the app.
What sync doesOwns the definition and rewrites it to match the code.Keeps it. Promotes it to system when the code declares the same name; refuses with a 400 when the types clash.

A runtime attribute added on a system object is merged back attribute by attribute: it stays where the user put it, and it becomes a system attribute only if your code later declares a compatible attribute with that name.

Retypes need a migration

Changing an attribute's type in code is ambiguous: is it a retype or a drop plus a create? The sync refuses to guess and answers 400 with the object and the migration to declare. Declare it on the builder, .migration(2, (m) => m.changeType(...)) or .renameAttribute(...), and the next sync replays it once. See SDK / Objects.

Sealed objects

An object marked .sealed() turns every undeclared runtime attribute into a standards diff failure unless .tolerate() names it; neither flag deletes user data. See SDK / Objects.

Demotion keeps records

Remove an object from the source, or remove the whole source, and the object is demoted to runtime: system: false, records untouched, attributes untouched. Users can then edit or delete it in the app. A sync demotes an object; it never deletes one.

Views are adopted and released

A view declared by a source is owned by it. When the source declares a view whose object and name already exist as a runtime view, the row is adopted: it becomes source-owned and workspace overlays on it are preserved. When a view leaves the source, the row is released back to runtime with its overlays. The sync never overwrites a user's view with a copy of its own.

Lifecycle over the API

schema.sync is a thin client over four routes.

RoutePermissionAnswer
GET /schema/sourcesworkspace:read{ sources: [{ sourceId, hash, appliedAt, objectNames, label, icon }] }
GET /schema/sources/:idworkspace:readOne summary. 404 with code SCHEMA_SOURCE_NOT_FOUND when the source was never pushed.
PUT /schema/sources/:idarchitect:update{ applied, hash, objects?, views? }
DELETE /schema/sources/:idarchitect:delete204. Owned objects are demoted, owned views released.

The push works like this:

  1. The client hashes { objects, views } (SHA-256 over canonical JSON) and reads GET /schema/sources/:id.
  2. Same hash: nothing is applied and the client answers { applied: false }. A changed label or icon is still stored; they are not part of the hash.
  3. Different hash or never pushed: PUT /schema/sources/:id with { hash, objects, views, label?, icon? }. The server recomputes the hash and answers 400 when it differs. It then checks ownership, validates every object and view with the builders' own rules, and applies the source under a per-workspace lock before persisting it.

The routes are exempt from rate limiting: a CI push carries the whole schema in one call.

Conflicts. An object or view already owned by the core, a bundle or another source is a 409 (DuplicateError), with a message like Object "services" is owned by source:legacy-site. The owner is always the other party; the client throws SchemaSourceConflictError with objectName and owner.

Limits. A source declares at most 50 objects and 200 views, and only views for its own objects. A source id is a lowercase slug (^[a-z0-9][a-z0-9-]{0,62}$).

From a terminal

standards sources list                     # id, hash, last apply, object names
standards sources remove status-page --yes # demote its objects, keep the records
standards pull                             # read the deployed source against the shared runtime
standards diff                             # exit 0 when equal, 1 on differences, 2 on error

standards pull and standards diff compare what is deployed with what users changed at runtime. They never modify files or the workspace. See CLI / Schema and sources.

Next steps