addedInput schema / properties / expected_revision
Added value: +{
+ "description": "Required when ESM source reuses an existing manifest.id. Pass the desired revision from get_app_source.",
+ "maximum": 9007199254740991,
+ "minimum": -9007199254740991,
+ "type": "integer"
+}
addedInput schema / properties / idempotency_key
Added value: +{
+ "description": "Required for ESM builds: 8–128 visible ASCII characters. Retry the exact request with the same key to recover its build; use a new key for changed source.",
+ "type": "string"
+}
addedInput schema / properties / migrate_contract
Added value: +{
+ "description": "Set true to migrate an existing legacy manifest.id to ESM, together with expected_revision. Source submissions do not roll back contracts. History can explicitly restore a retained validated existing-contract revision.",
+ "type": "boolean"
+}
changedInput schema / properties / module / description
Previous value: -"ES module source for the app backend. Export a `manifest`; to persist any state its `capabilities.imports` must include \"buildy:storage/kv@1.0\" (without it `env.storage` is undefined and every read/write throws `storage capability not granted`). All persistence goes through `env.storage` using Workers KV semantics — `get(key)`, `put(key, value)`, `delete(key)`, `list()`; `.set`/`.add`/`.write`/`.setItem`/`.removeItem` do not exist and throw `TypeError: env.storage.<x> is not a function`. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Use env.storage for ALL persistence — it is the only storage that survives inside Claude/ChatGPT and syncs across devices. Do NOT keep app data or user state in localStorage/sessionStorage/IndexedDB: those APIs are empty inside chat hosts, so their data silently vanishes there (the most common cause of an app that appears not to save). The \"buildy:browser/storage@1.0\" capability (claim-gated) unlocks them but only for throwaway, web-only caching; anything the user expects to keep belongs in env.storage. Export `routes` (recommended) or a default `fetch` handler serving `/api/<opName>`, each returning `{ ok: true, value }` or `{ ok: false, error: { kind, message } }`. Apps that use a sensitive browser capability must declare its import in `capabilities.imports` — \"buildy:browser/microphone@1.0\" (getUserMedia audio), \"buildy:browser/camera@1.0\" (getUserMedia video), \"buildy:browser/geolocation@1.0\", \"buildy:browser/clipboard-read@1.0\" (reading the clipboard), \"buildy:browser/display-capture@1.0\" (getDisplayMedia screen share), \"buildy:browser/midi@1.0\" (Web MIDI, navigator.requestMIDIAccess), \"buildy:browser/device-motion@1.0\" (device orientation/motion: DeviceOrientationEvent/DeviceMotionEvent + iOS requestPermission), \"buildy:browser/ambient-light@1.0\" (ambient light, new AmbientLightSensor), or \"buildy:browser/storage@1.0\" (native client storage — localStorage/sessionStorage/IndexedDB — web-only); access is granted only after the app is claimed/authenticated. To call external HTTPS APIs from backend code, declare \"buildy:network/fetch@1.0\" — backend `fetch` is blocked by default and this enables it (public-only: private/loopback/cloud-metadata addresses stay blocked; claim-gated). Restrict it to the hosts the app needs with `manifest.capabilities.fetchHosts` (concrete https hosts, like imageHosts) — default-deny, any non-listed host is blocked; omit to allow any public host. For an endpoint that needs an API key, declare \"buildy:secrets/fetch@1.0\" to get `env.fetch` (claimed apps only) — a sealed outbound fetch that substitutes `{{secret:NAME}}` references in request HEADER values host-side, so the key never enters app source or the sandbox; the app OWNER sets the value in the dashboard at `/app/<id>/secrets` and the agent only references the NAME. Never embed API keys in source. To render remote images, list each https host in `manifest.capabilities.imageHosts`. To make an image render in ANY host — standalone, ChatGPT, AND Claude inline (their injected CSP blocks a cross-origin `<img src>`) — set the src from `const src = await window.buildy.images.load(remoteUrl)` (it fetches through Charming and returns a `data:` URL every embed CSP allows). `window.buildy.images.proxy(remoteUrl)` returns a same-origin proxy URL that works standalone/ChatGPT but NOT in Claude inline; prefer `images.load(...)` when the app may be embedded. Both enforce the imageHosts allowlist; neither bypasses it."New value: +"Eligible ESM authors may instead select `$schema: \"https://charm.ing/schema/app-manifest/2026-09-05.json\"`, declare target-specific `dependencies.server` and `dependencies.client`, and use ordinary package imports plus exact versioned platform imports. For ESM persistence, use `import { kv } from 'charming:storage/kv@1.0'` and call `kv.get`/`kv.put` inside route handlers; omit `capabilities`. ESM creation takes its name from `manifest.meta.name`; omit `display_name`. For the existing contract, export a strict canonical `manifest` with `$schema: \"https://charm.ing/schema/app-manifest/2026-07-31.json\"`, `id`, and `meta: { name, icon? }`. The server may insert the exact schema URL on create when the rest of the source is canonical. Unknown manifest keys are rejected. To persist state in the existing contract, `capabilities.imports` must include \"charming:storage/kv@1.0\" (without it `env.storage` is undefined and every read/write throws `storage capability not granted`). A route handler in `export const routes = [...]` receives `(input, { env, ctx, request })` and returns exactly the value declared by `outputSchema`; for an array schema, use `handler: async (_input, { env }) => (await env.storage.get(\"key\")) ?? []`. Charming creates the transport envelope. Do not add a `{ ok, value }` or `{ value }` envelope unless those fields belong to `outputSchema` itself. A named context reads storage as `context.env.storage`. The optional unmatched-request fallback has the separate signature `export default { fetch(request, env, ctx) { ... } }`, where the second argument is the environment itself; it adds no discoverable route metadata. `env.user` is always present, not gated by any import: the caller's public identity (`{ id, handle?, name?, image? }`) or `null`. It lives only on `env` — read `env.user` (or `context.env.user`); there is no `ctx.user`. Full reference: call read_docs with path `llms-full.txt` (https://charm.ing/docs/llms-full.txt). For the existing contract, persistence goes through `env.storage` using Workers KV semantics — `get(key)`, `put(key, value)`, `delete(key)`, `list()`; `.set`/`.add`/`.write`/`.setItem`/`.removeItem` do not exist and throw `TypeError: env.storage.<x> is not a function`. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Use env.storage for ALL persistence — it is the only storage that survives inside Claude/ChatGPT and syncs across devices. Do NOT keep app data or user state in localStorage/sessionStorage/IndexedDB: those APIs are empty inside chat hosts, so their data silently vanishes there (the most common cause of an app that appears not to save). The \"charming:browser/storage@1.0\" capability (claim-gated) unlocks them but only for throwaway, web-only caching; anything the user expects to keep belongs in env.storage. Export a `routes` array with unique `op` values and `handler` functions. Canonical route fields are `inputSchema`, `outputSchema`, and `annotations`; `method` defaults to `POST`, `path` defaults to `/api/<op>`, input defaults to a closed empty-object schema, and `public` defaults to true. Set all four MCP annotation hints when their defaults do not fit; Charming does not infer them from the HTTP method. A default `fetch` handler is an unmatched-request fallback only and is not discoverable. The following capability declarations apply only to the existing contract. Apps that use a sensitive browser capability must declare its import in `capabilities.imports` — \"charming:browser/microphone@1.0\" (getUserMedia audio), \"charming:browser/camera@1.0\" (getUserMedia video), \"charming:browser/geolocation@1.0\", \"charming:browser/clipboard-read@1.0\" (reading the clipboard), \"charming:browser/display-capture@1.0\" (getDisplayMedia screen share), \"charming:browser/midi@1.0\" (Web MIDI, navigator.requestMIDIAccess), \"charming:browser/device-motion@1.0\" (device orientation/motion: DeviceOrientationEvent/DeviceMotionEvent + iOS requestPermission), \"charming:browser/ambient-light@1.0\" (ambient light, new AmbientLightSensor), or \"charming:browser/storage@1.0\" (native client storage — localStorage/sessionStorage/IndexedDB — web-only); access is granted only after the app is claimed/authenticated. To call external HTTPS APIs from backend code, declare \"charming:network/fetch@1.0\" and list each exact origin in `manifest.permissions.server.fetch`; both are required and public-only. For an endpoint that needs an API key, declare \"charming:secrets/fetch@1.0\" to get `env.fetch` (claimed apps only) — a sealed outbound fetch that substitutes `{{secret:NAME}}` references in request HEADER values or query-parameter VALUES host-side (never a parameter name, the host, path, fragment, or body), so the key never enters app source or the sandbox; write the placeholder literally in the URL string — `URLSearchParams.set(...)` or `encodeURIComponent(...)` percent-encodes it first and it will NOT resolve; the app OWNER opens App settings, then Secrets, at `/<owner-handle>/~/apps/<app-name>/settings/secrets`, while `/app/<id>/secrets` remains the machine HTTP API. The agent only references the NAME. Never embed API keys in source. To render remote images, list each exact https origin in `manifest.permissions.browser[\"img-src\"]`. To make an image render in ANY host — standalone, ChatGPT, AND Claude inline (their injected CSP blocks a cross-origin `<img src>`) — set the src from `const src = await window.charming.images.load(remoteUrl)` (it fetches through Charming and returns a `data:` URL every embed CSP allows). `window.charming.images.proxy(remoteUrl)` returns a same-origin proxy URL that works standalone/ChatGPT but NOT in Claude inline; prefer `images.load(...)` when the app may be embedded. Both enforce the declared origins; neither bypasses them."
addedInput schema / properties / team_id
Added value: +{
+ "description": "Optional destination team id. Only a team owner or admin can create an App there. Omit it to create a personal App.",
+ "type": "string"
+}
changedInput schema / properties / ui / description
Previous value: -"Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.buildy.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.buildy.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest \"AI-generated app\" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.buildy.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the charming:app-guide prompt for a canonical example."New value: +"Under the explicit ESM contract, this is an ES module: import declared client packages and import { api, onStateChange } from \"charming:ui/app@1.0\" to call this app and subscribe to its state changes. Under the existing contract, follow the classic JavaScript rules below. Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.charming.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.charming.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest \"AI-generated app\" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.charming.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the charming:app-guide prompt for a canonical example."
addedOutput schema / anyOf
Added value: +[
+ {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "additionalProperties": false,
+ "properties": {
+ "advisories": {
+ "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.",
+ "items": {
+ "additionalProperties": false,
+ "properties": {
+ "data": {
+ "additionalProperties": {},
+ "description": "Kind-specific structured payload. Shape varies per advisory kind.",
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "doc_url": {
+ "description": "Optional docs pointer for this advisory kind.",
+ "type": "string"
+ },
+ "kind": {
+ "description": "Stable advisory identifier (e.g. \"legacy-bridge\").",
+ "type": "string"
+ },
+ "severity": {
+ "description": "Severity; omitted advisories are treated as 'info'.",
+ "enum": [
+ "info",
+ "warn"
+ ],
+ "type": "string"
+ },
+ "summary": {
+ "description": "Agent-facing summary. Self-sufficient; no extra context required.",
+ "type": "string"
+ },
+ "userSummary": {
+ "description": "End-user-facing summary. Set when the advisory should render in-app.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "summary"
+ ],
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "appName": {
+ "description": "URL-safe slug for the app, unique per owner. Distinct from manifestId and displayName, and stable across edits — use rename_app to change it (the title changing does NOT change the slug).",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "icon": {
+ "additionalProperties": false,
+ "description": "The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`.",
+ "properties": {
+ "bg": {
+ "description": "The icon background as a hex color (e.g. \"#1d8a4e\").",
+ "type": "string"
+ },
+ "emoji": {
+ "description": "The single emoji rendered on the icon.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "emoji",
+ "bg"
+ ],
+ "type": "object"
+ },
+ "id": {
+ "description": "UUID of the created or updated app.",
+ "type": "string"
+ },
+ "ok": {
+ "const": true,
+ "description": "Indicates success. Errors arrive as content with isError:true.",
+ "type": "boolean"
+ },
+ "revision": {
+ "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.",
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "shareUrl": {
+ "description": "The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls.",
+ "type": "string"
+ },
+ "url": {
+ "description": "Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead.",
+ "type": "string"
+ },
+ "warnings": {
+ "description": "Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "ok",
+ "id",
+ "url",
+ "shareUrl",
+ "revision",
+ "icon"
+ ],
+ "type": "object"
+ },
+ {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "additionalProperties": false,
+ "properties": {
+ "acceptedAt": {
+ "type": "string"
+ },
+ "activeRevision": {
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "appId": {
+ "description": "Existing target app ID, or the created app ID after publication.",
+ "type": "string"
+ },
+ "attempts": {
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "buildId": {
+ "description": "Durable build ID. Use get_app_build to inspect progress or source.",
+ "type": "string"
+ },
+ "deadline": {
+ "type": "string"
+ },
+ "desiredRevision": {
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "elapsedMs": {
+ "minimum": 0,
+ "type": "number"
+ },
+ "error": {
+ "additionalProperties": false,
+ "properties": {
+ "column": {
+ "maximum": 9007199254740991,
+ "minimum": -9007199254740991,
+ "type": "integer"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "line": {
+ "maximum": 9007199254740991,
+ "minimum": -9007199254740991,
+ "type": "integer"
+ },
+ "message": {
+ "type": "string"
+ },
+ "retryable": {
+ "type": "boolean"
+ },
+ "specifier": {
+ "type": "string"
+ },
+ "target": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "message",
+ "retryable"
+ ],
+ "type": "object"
+ },
+ "finishedAt": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "idempotencyExpiresAt": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "inputDigest": {
+ "type": "string"
+ },
+ "inspectionExpiresAt": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "intent": {
+ "description": "The accepted operation: create, update, migrate, restore, or copy.",
+ "type": "string"
+ },
+ "lockDigest": {
+ "type": "string"
+ },
+ "lockState": {
+ "enum": [
+ "locked",
+ "unresolved"
+ ],
+ "type": "string"
+ },
+ "ok": {
+ "const": true,
+ "type": "boolean"
+ },
+ "queueDeadline": {
+ "type": "string"
+ },
+ "resolvedDependencies": {
+ "additionalProperties": false,
+ "properties": {
+ "client": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "server": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "server",
+ "client"
+ ],
+ "type": "object"
+ },
+ "retryAfterSeconds": {
+ "description": "Wait at least this many seconds before polling again.",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991,
+ "type": "integer"
+ },
+ "revision": {
+ "description": "Published app source revision.",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991,
+ "type": "integer"
+ },
+ "source": {
+ "additionalProperties": false,
+ "description": "Exact accepted source, returned only when include_source is true.",
+ "properties": {
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "module": {
+ "type": "string"
+ },
+ "styles": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ui": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "module",
+ "ui",
+ "styles",
+ "description"
+ ],
+ "type": "object"
+ },
+ "sourceEtag": {
+ "description": "ETag identifying the immutable input accepted for this build.",
+ "type": "string"
+ },
+ "state": {
+ "enum": [
+ "queued",
+ "resolving",
+ "building",
+ "validating",
+ "published",
+ "failed",
+ "superseded",
+ "canceled",
+ "expired"
+ ],
+ "type": "string"
+ },
+ "statusUrl": {
+ "description": "Authenticated HTTP status URL for this build.",
+ "type": "string"
+ },
+ "updatedAt": {
+ "type": "string"
+ },
+ "url": {
+ "description": "App URL. Present only after successful publication.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "ok",
+ "buildId",
+ "intent",
+ "state",
+ "sourceEtag",
+ "statusUrl",
+ "attempts",
+ "acceptedAt",
+ "updatedAt",
+ "finishedAt",
+ "queueDeadline",
+ "deadline",
+ "inspectionExpiresAt",
+ "idempotencyExpiresAt",
+ "elapsedMs",
+ "lockState",
+ "inputDigest"
+ ],
+ "type": "object"
+ }
+]
addedOutput schema / properties / acceptedAt
Added value: +{
+ "type": "string"
+}
addedOutput schema / properties / activeRevision
Added value: +{
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+}
changedOutput schema / properties / advisories / description
Previous value: -"Structured advisories attached when the tool detected a non-fatal authoring issue (e.g. legacy bridge usage). Each advisory.summary is also appended to the text content for the LLM path."New value: +"Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path."
addedOutput schema / properties / appId
Added value: +{
+ "description": "Existing target app ID, or the created app ID after publication.",
+ "type": "string"
+}
removedOutput schema / properties / appName / anyOf
Removed value: -[
- {
- "type": "string"
- },
- {
- "type": "null"
- }
-]
addedOutput schema / properties / appName / type
Added value: +[
+ "string",
+ "null"
+]
addedOutput schema / properties / attempts
Added value: +{
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+}
addedOutput schema / properties / buildId
Added value: +{
+ "description": "Durable build ID. Use get_app_build to inspect progress or source.",
+ "type": "string"
+}
addedOutput schema / properties / deadline
Added value: +{
+ "type": "string"
+}
addedOutput schema / properties / desiredRevision
Added value: +{
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+}
addedOutput schema / properties / elapsedMs
Added value: +{
+ "minimum": 0,
+ "type": "number"
+}
addedOutput schema / properties / error
Added value: +{
+ "additionalProperties": false,
+ "properties": {
+ "column": {
+ "maximum": 9007199254740991,
+ "minimum": -9007199254740991,
+ "type": "integer"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "line": {
+ "maximum": 9007199254740991,
+ "minimum": -9007199254740991,
+ "type": "integer"
+ },
+ "message": {
+ "type": "string"
+ },
+ "retryable": {
+ "type": "boolean"
+ },
+ "specifier": {
+ "type": "string"
+ },
+ "target": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "message",
+ "retryable"
+ ],
+ "type": "object"
+}
addedOutput schema / properties / finishedAt
Added value: +{
+ "type": [
+ "string",
+ "null"
+ ]
+}
changedOutput schema / properties / icon / description
Previous value: -"The effective home-screen icon stored for the app, after normalization. Read this back to confirm what stuck: if you sent a `manifest.icon` and this is the default `{ emoji: \"🧱\", bg: \"#3b82f6\" }`, your icon was invalid and rejected — see `warnings`."New value: +"The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`."
addedOutput schema / properties / idempotencyExpiresAt
Added value: +{
+ "type": [
+ "string",
+ "null"
+ ]
+}
addedOutput schema / properties / inputDigest
Added value: +{
+ "type": "string"
+}
addedOutput schema / properties / inspectionExpiresAt
Added value: +{
+ "type": [
+ "string",
+ "null"
+ ]
+}
addedOutput schema / properties / intent
Added value: +{
+ "description": "The accepted operation: create, update, migrate, restore, or copy.",
+ "type": "string"
+}
addedOutput schema / properties / lockDigest
Added value: +{
+ "type": "string"
+}
addedOutput schema / properties / lockState
Added value: +{
+ "enum": [
+ "locked",
+ "unresolved"
+ ],
+ "type": "string"
+}
removedOutput schema / properties / ok / description
Removed value: -"Indicates success. Errors arrive as content with isError:true."
addedOutput schema / properties / queueDeadline
Added value: +{
+ "type": "string"
+}
addedOutput schema / properties / resolvedDependencies
Added value: +{
+ "additionalProperties": false,
+ "properties": {
+ "client": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ },
+ "server": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "propertyNames": {
+ "type": "string"
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "server",
+ "client"
+ ],
+ "type": "object"
+}
addedOutput schema / properties / retryAfterSeconds
Added value: +{
+ "description": "Wait at least this many seconds before polling again.",
+ "exclusiveMinimum": 0,
+ "maximum": 9007199254740991,
+ "type": "integer"
+}
addedOutput schema / properties / revision
Added value: +{
+ "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.",
+ "maximum": 9007199254740991,
+ "minimum": 0,
+ "type": "integer"
+}
addedOutput schema / properties / source
Added value: +{
+ "additionalProperties": false,
+ "description": "Exact accepted source, returned only when include_source is true.",
+ "properties": {
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "module": {
+ "type": "string"
+ },
+ "styles": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "ui": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "module",
+ "ui",
+ "styles",
+ "description"
+ ],
+ "type": "object"
+}
addedOutput schema / properties / sourceEtag
Added value: +{
+ "description": "ETag identifying the immutable input accepted for this build.",
+ "type": "string"
+}
addedOutput schema / properties / state
Added value: +{
+ "enum": [
+ "queued",
+ "resolving",
+ "building",
+ "validating",
+ "published",
+ "failed",
+ "superseded",
+ "canceled",
+ "expired"
+ ],
+ "type": "string"
+}
addedOutput schema / properties / statusUrl
Added value: +{
+ "description": "Authenticated HTTP status URL for this build.",
+ "type": "string"
+}
addedOutput schema / properties / updatedAt
Added value: +{
+ "type": "string"
+}
changedOutput schema / properties / url / description
Previous value: -"Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead."New value: +"App URL. Present only after successful publication."
removedOutput schema / properties / version
Removed value: -{
- "description": "Monotonic int version of the app. 0 on truly-legacy rows that predate versioning (still editable — pass `expected_version: 0`); otherwise a positive int bumped on every successful update_app/PUT. Pass back as `expected_version` on edits-aware update_app and as `If-Match: \"v<N>\"` on PATCH /app/:id/source.",
- "maximum": 9007199254740991,
- "minimum": -9007199254740991,
- "type": "integer"
-}
changedOutput schema / properties / warnings / description
Previous value: -"Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches, or when a provided `manifest.icon` was invalid and coerced to the default. The write succeeded; fix by adding the backend op, renaming the UI call, or correcting the icon `{ emoji, bg }`."New value: +"Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field."
changedOutput schema / required
Previous value: -[
- "ok",
- "id",
- "url",
- "shareUrl",
- "version",
- "icon"
-]New value: +[
+ "ok"
+]