changedInput schema / properties / edits / description
Previous value: -"Edits path. Array of exact-string find/replace operations applied atomically. Pass `expected_version` alongside to gate optimistic-concurrency. Mutually exclusive with full-source fields (`module` / `ui` / `styles`)."New value: +"Edits path. Array of exact-string find/replace operations applied atomically. Pass the last-read revision through `expected_revision` to gate optimistic concurrency. Mutually exclusive with full-source fields (`module` / `ui` / `styles`)."
addedInput schema / properties / expected_revision
Added value: +{
+ "description": "Revision precondition. Required for every ESM save and for existing-contract edits[]. Pass revision from your last get_app_source response. A new request against a stale desired revision fails with revision_mismatch; an identical idempotent retry returns its original build.",
+ "maximum": 9007199254740991,
+ "minimum": -9007199254740991,
+ "type": "integer"
+}
changedInput schema / properties / expected_version / description
Previous value: -"Required when `edits[]` is present. Pass the `version` from your last get_app_source or update_app response. If the stored version has advanced, the edit is rejected with `version_mismatch` and the current version is surfaced so you can re-read and retry."New value: +"Deprecated compatibility input. Use `expected_revision`; while legacy hosts remain active, expected_version maps to the same revision precondition."
addedInput schema / properties / idempotency_key
Added value: +{
+ "description": "Required for ESM saves. Retry the exact request with the same key; use a new key and current expected_revision for a new save.",
+ "type": "string"
+}
addedInput schema / properties / migrate_contract
Added value: +{
+ "description": "Set true only when replacing a legacy app with a complete valid dated manifest and routes contract. 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: -"Full-source path only. Optional replacement ES module source. Same contract as create_app: must export a `manifest` with capabilities.imports including \"buildy:storage/kv@1.0\" for any app that persists state, plus route handlers that use env.storage.get/put. default.fetch is optional; Charming supplies a generic 404 handler when it is absent. Keep all persisted state in env.storage — do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`."New value: +"Full-source path only. Optional replacement ES module source. Same selected contract as create_app. For the existing contract, declare capabilities.imports including \"charming:storage/kv@1.0\" for persistence. For ESM, keep the exact ESM schema and import { kv } from \"charming:storage/kv@1.0\"; omit capabilities. 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; Charming supplies a generic 404 handler when it is absent. `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). Keep persisted state in backend storage (env.storage for the existing contract, imported kv for ESM); do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`. Migrating a legacy app to the existing dated contract requires the complete canonical manifest with the exact dated `$schema: \"https://charm.ing/schema/app-manifest/2026-07-31.json\"` plus `migrate_contract: true`; on this full-source path only, an omitted `$schema` is inserted automatically, but a wrong one still fails. ESM migration instead selects `https://charm.ing/schema/app-manifest/2026-09-05.json` through full-source or edits and requires `migrate_contract: true`, `expected_revision`, and `idempotency_key`."
changedInput schema / properties / ui / description
Previous value: -"Full-source path only. Optional replacement frontend JavaScript. Same contract as create_app: must (a) populate #app 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(). 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. Cap the reading measure on an INNER wrapper only for text-heavy views, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Mutually exclusive with `edits`."New value: +"Full-source path only. Optional replacement frontend JavaScript. Follow the create_app rules for the selected contract. Existing-contract UI must (a) populate #app 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(). 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. Cap the reading measure on an INNER wrapper only for text-heavy views, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Mutually exclusive with `edits`."
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"
+]