Skip to navigation

Workspaces And Changesets

Workspaces and changesets are the safe path for design work. They keep exploratory edits away from published runtime behavior until validation and review are complete.

Workspace

A workspace is an isolated design area for one app. Use it to add or change commands, events, projections, policies, permissions, adapters, agents, or pages without disturbing the published model.

Keep the workspace focused. A workspace called invoice-approval-v2 should not also contain unrelated page cleanup or adapter experiments.

Agent artifacts are stored as model:agent:<stable AgentSpec.id>. Design MCP read and patch tools also accept model:agent:<AgentSpec.name> for existing agents and return the resolved id-backed artifact ref.

Workspace lists return a bounded, newest-updated-first page from the maintained summary cache. The default page contains at most 25 workspaces and limit is capped at 100. Pass pagination.nextCursor back unchanged as cursor with the same app and filter values to continue, or use workspaceId for an exact bounded lookup. Set compact: true to omit the legacy top-level structured-content mirror while preserving JSON text and the canonical payload under data. Candidate filtering is bounded, so sparse pages can return fewer than limit matches while still providing nextCursor; totalCount remains null until totalIsExact is true. Pagination reports truncated: true if the next raw cursor start would exceed the defensive position-10,000 ceiling. Exact workspaceId lookup remains available for a known older workspace.

Each list summary has a Git-style divergence view. aheadCount is the number of workspace-local artifact changes. The cached list path reports behindCount as null because it does not load and diff the full published graph; isBehind reports identity-based freshness only when the latest production identity shares a release ID or has compatible content-hash provenance. A validation target hash is not compared with a canonical published artifact-graph hash: that case reports isBehind: null (unknown), not behind. List diagnostics include the latest Release/hash provenance and comparison policy. Use the exact workspace-status read when you need the full published-artifact behind count. List filters can focus on active, closed, behind, current, or unknown-freshness workspaces. By default, workspaces whose latest changeset has already been promoted are hidden from active lists but remain available in closed/history views.

Production stores an active custom Page Source as its slug and exact TSX source, while the Release snapshot retains the full validated Artifact node, including resource and SDK dependencies. Creating a Workspace from that Release restores those dependencies from the snapshot. Exact status checks compare the materialized Page Source bytes and still require complete Page Resource and published-model coherence; missing or changed runtime bytes fail closed instead of producing a guessed base.

Workspace runtime execution can validate a new wake_agent policy before promotion: commands, events, policy triggers, target agent references, rendered wake messages, and Workspace-only agent wakes all run against the workspace model. When a wake_agent policy targets an agent that only exists in the workspace, execute_command with waitForPolicies: true provisions a temporary Workspace-scoped agent container on demand, routes the wake to it, and removes that preview container after the wake completes. If preview provisioning is unavailable or fails before the wake can run, the command returns a non-failing wake_agent.workspace_preview_unavailable diagnostic instead of a generic container error.

Workspace runtime execution also uses unpromoted custom_code policy handlers. If a command triggers a Workspace custom_code policy, execute_command resolves the worker from the materialized Workspace policy-code artifact rather than any promoted Production deployment. This lets new and edited policy handlers be validated end-to-end before promotion. With waitForPolicies: true, the response includes a policySettlements entry for each exact durable invocation created by the command. completed includes handlers that successfully return null; failed and not_started carry a stable error code plus any available worker, correlation, and policy-execution identities. A Workspace execution never falls back to promoted policy code.

Create a new custom-code policy in one upsert_workspace_artifact(kind: "model:policy") call by including its inline code, package dependencies, and any finite targetCommands. Cessy validates the handler and atomically creates the internal metadata/source pair, so callers do not need a metadata-only write followed by an intermediate-hash replace_policy_handler call. Continue to use patch_policy_handler or replace_policy_handler for edits to an existing handler.

A new dispatch_command policy uses the same Workspace upsert with commandType and commandMapping, but no code or handler hash; Cessy stores it as a single model:policy artifact.

Literal command type values and homogeneous batch commandType values are discovered automatically. If either value is computed dynamically or otherwise unresolved, pass an explicit finite targetCommands list to replace_policy_handler, or patch /targetCommands together with the code. Changeset validation blocks promotion when a detected command is missing, a dynamic target has no explicit finite declaration, or the policy’s runtime allowedCommands does not enforce the same set.

For side-effect-only handlers, omit return or directly return null or []. An indirect zero-target result such as return commands is treated as unresolved and requires an explicit empty targetCommands attestation. An empty attestation is rejected when the handler definitely returns a dynamic type or batch commandType field.

targetCommands is a finite upper bound, not a requirement that every invocation dispatches. A conditional handler may declare targets and still return null or [] while it waits for a settle condition; that invocation is recorded as a successful no-command result, not an error.

Each awaited ctx.getProjection(...) call bypasses response caches and executes a new live read against the active Production or Workspace runtime source. A bounded polling loop can therefore observe projection writes that commit while the same handler is waiting. Separate calls, including calls to different projections, are not one atomic snapshot; require two consecutive stable exact reads before returning a completion command. Add a delay or backoff between attempts and set a finite attempt/deadline limit. Projection list reads remain capped at 1,000 documents per call, and the cache bypass applies only to the internal policy projection callback.

Existing policies with blockedCommands but no allowedCommands remain valid when their stored target declaration is complete and every handler target is statically resolvable. Runtime enforces the stored targets as an upper bound before applying the blocklist. A handler with dynamic targets must carry an explicitly attested finite target set and cannot rely on the blocklist alone.

For older published handlers that have targetCommands but no allowedCommands, runtime uses the stored targets as a finite compatibility allowlist. Commands from a previously undetected dynamic branch are rejected until you replace the handler and explicitly attest its complete target list. Older custom-code handlers with neither stored targetCommands nor a nonempty allowedCommands allowlist fail closed at dispatch time and must be replaced to establish finite target metadata.

These canonical target-declaration errors do not make the whole Workspace unavailable. Pages, projection reads, and unrelated command flows can still use the isolated Workspace runtime while you repair the handlers. Policies with those errors are omitted from the isolated runtime snapshot, so their worker code does not run and cannot perform external side effects. Their stored Workspace artifacts remain available for remediation. Valid handlers continue to use the normal Workspace HTTP policy and command guards.

ChangeSet validation and production activation compare canonical target-declaration and allowlist findings with the Workspace base. An exact inherited finding is reported as non-blocking debt only when both the policy metadata and handler artifacts are unchanged. A new or changed finding blocks, and touching either model:policy:<name> or policy-code:<name> makes matching inherited debt block again until the handler is remediated. Invalid policy-code attachments and unrelated configuration errors remain blocking.

Changeset

A changeset packages workspace changes for validation, review, and promotion. It should tell reviewers:

  • what business capability changed
  • which artifacts changed
  • which validation errors are fixed or remain
  • which runtime contracts will change
  • which environment is the promotion target

Omitting refs when creating a changeset gives it full-workspace scope. Passing explicit refs creates an immutable selected scope. Cessy closes that selection over changed dependencies (or fails when dependency inclusion was not authorized), applies the captured unit to the pinned base, and validates exactly the graph that promotion would activate. Unselected workspace edits are neither deleted nor silently included; they remain available for a later changeset and can change without making the selected unit stale.

Promotion still verifies the selected refs and dependency closure against the source workspace. A captured artifact that changed or disappeared yields stale_changeset_validation with a machine-readable reasonCode. Full/default changesets retain their existing behavior: any later workspace-diff change requires a fresh changeset and validation. Exact Production-base and environment-pointer leases apply equally to both scopes.

For Custom Pages, validation separates technical integrity from Design System guidance. Syntax, imports, resource/security boundaries, CSS parse/encoding failures, and unavailable validation remain blocking. Visual conventions, hard-coded values, undeclared/local CSS custom properties, and unknown additional component props are advisory. Missing required props and incompatible value shapes block only when the page is changed or transitively affected; identical debt on unchanged, unaffected pages is reported as a warning and does not block the changeset. A default Page component that requires host-provided props follows the same affected-page ratchet: changed or affected sources fail because the hosted runtime mounts <PageComponent /> without props, while unrelated inherited debt remains non-blocking. Use useParams() or useDocument() for hosted context.

Creating a ChangeSet also repairs legacy Workspace Page graphs that reference published modules, assets, or Design System artifacts without owning them. The server verifies and pins only the reachable transitive resource closure, proves the projected ChangeSet is complete, and only then persists the Workspace repair. The response reports the additions as materializedDependencyRefs. Explicit Workspace deletions and missing non-Page dependencies still fail closed; no partial repair is stored.

If validate_changeset returns workspace_database_admission_unavailable, call get_changeset before retrying. A matching Validation Artifact means the immutable validation committed even if the original response was lost. When no match exists, follow retryAfterSeconds and retry the exact same request.

AI builder workflow

When using Claude, Codex, or another agent through Design MCP, ask it to:

  1. inspect the current app
  2. create or select a focused workspace
  3. make the smallest coherent set of edits
  4. validate before asking to promote
  5. summarize model, API, permission, and runtime impact

Agents should not promote broad changes without an explicit builder approval step.

Promotion discipline

During the bounded v1/vNext migration window, a ChangeSet can be promoted even when the current Production Release came from a vNext Candidate. Rebase the Workspace onto that exact Release and create a fresh validation first. The ChangeSet and Candidate paths share one current Release pointer and publication fence, so stale work cannot overwrite the winner. Raw hosting writes and arbitrary historical Legacy rollback are not part of this compatibility path. There is one bounded cutover recovery: an app administrator can use Restore previous Release when the current first vNext Candidate Release points to an exact retained Legacy base. Cessy reactivates that immediate base snapshot without creating another Candidate Release. The raw Legacy rollback tool stays blocked, and no other historical Legacy Release can be selected.

Promotion captures the current pointer and fence before it claims Production, then proves the complete current target once under that claim. If an exact storage read is temporarily unavailable before activation, the tool returns target_observation_unavailable with retryable: true and releases its claim. Retry the same immutable ChangeSet and Validation Artifact with the same expected Production Release. Do not recreate the Workspace or repair Production data. The response includes exact inspect_design_target_diagnostic arguments and a matching ces:// diagnostic Resource. Inspection is read-only and reports whether the condition is still active, has been superseded by a newer bounded diagnostic, or has resolved. The same inspection contract applies when a temporary read blocks Workspace creation or rebase. Proven missing or mismatched Release lineage still fails closed with target_not_coherent and is not made retryable by this contract.

Long Legacy release histories do not by themselves make Production incoherent. Cessy limits historical Page ownership discovery to 256 Releases, including the current Release. If no Candidate ownership boundary is proven within that budget, it compares the complete current bound snapshot with live Page materializations. Missing or changed owned Page bytes still block rebase and promotion; observed lineage corruption remains an error.

If a request disappears after the fence reaches activating, call get_workspace_status. While the deadline is live, wait. When recovery reports resume_reconciliation, retry promote_changeset with the same ChangeSet, Validation Artifact, and expected Production Release. Cessy verifies the content-addressed Release identity in a bounded interval immediately before the durable fence claim, deterministically rebuilds and verifies the prepared Snapshot, and resumes that same operation. An already committed Domain event is reconciled through the bounded Page-deployment path; otherwise deterministic preparation is continued without duplicating Page-source state. For that exact activating operation, an owned live Page Source or Resource may be at the bound base or at the reconstructed candidate bytes while the Domain is still at the base. Once the Domain has moved, every owned Page value must already match the candidate. Any other live value still fails closed with target_not_coherent. A different ChangeSet cannot take over the activation, and an unverified activating fence cannot be force cleared.

If required Page deployment reaches a temporary database-admission deadline, promote_changeset returns workspace_database_admission_unavailable with the required_page_deployment phase, retryAfterSeconds, an exact retry payload, and a recovery lookup. Use get_changeset to obtain the Workspace ID, inspect get_workspace_status, then retry the unchanged promotion after the indicated delay. Do not call target_claim_recover for an unknown post-activation claim—the exact promotion retry performs bounded, idempotent Page reconciliation.

Before promotion, verify:

  • validation passes
  • generated OpenAPI changes are expected
  • permission changes are intentional
  • projection rebuild needs are understood
  • custom pages and agents affected by the change are named

After promotion, run runtime smoke tests in the target environment.