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:
- inspect the current app
- create or select a focused workspace
- make the smallest coherent set of edits
- validate before asking to promote
- 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.