# 23 · Release Readiness, Versioning, and Rollback

Read this after the package archive is built and before an operator installs
or updates the extension in Cedros — and again for every extension update that
changes manifest metadata, runtime packages, data shape, routes, jobs, or
provider behavior. It covers the release readiness packet and operator
handoff, then the versioning, upgrade, downgrade, disable, and rollback plan
for the extension family.

## Current model

- Cedros local package intake validates manifest/bootstrap metadata first.
- Admin runtime behavior depends on compiled browser ESM included in the ZIP.
  Server/native behavior depends on published contracts or explicit host
  coordination.
- Install/enable registers manifest-backed admin and dashboard surfaces
  immediately. Page seeds and job schedules are provisioned by the server
  inside the enable request, so seeded public routes exist when enable
  resolves. Docs, form, and policy starters still provision
  **asynchronously** in the admin shell after enable — the host UI gates
  "installed" on that work, and smoke tests must wait for it to settle before
  checking those records.
- Some declared server, native, migration, settings, provider, database, or
  content-model work may still require host binding before it is live.
- Operators need a short, concrete handoff that separates "ready to install"
  from "ready to execute live behavior."
- The standard local-intake ZIP is not an offline dependency bundle. If the
  release targets an air-gapped or zero-network server environment, the release
  packet must call out the separate offline runtime bundle and its verification
  status.
- One extension family keeps one stable `extensionId`.
- The manifest `version` and package-family versions are the operator-visible
  release identity.
- Stable ids matter more than display text. Existing ids should not be renamed
  casually.
- Cedros can compare inventory metadata and installed versions. Route/job
  execution changes are versioned with the packaged component. There is no
  host-run extension migration, upgrade, or rollback hook: `migrations[]` ids
  are review metadata, and declared content models are registered additively.
  Any data reshaping must be replay-safe work inside your own routes/jobs;
  native hooks and sidecar service changes remain host-coordinated.
- Disable/remove is not the same as deleting extension-owned data.

## Release packet

- extension id, display name, version
- archive filename and checksum when available
- package-family source status
- supply-chain evidence status and marketplace verification result if Cedros
  has computed one
- required services/capabilities/providers
- required official/composable contract ids and published status
- included surfaces
- host-coordinated work status
- offline runtime bundle status (`not required`, `provided`, or `blocked`)
- validation command results
- install/enable smoke checklist
- rollback and disable instructions
- known risks and accepted gaps

For a small extension, the baseline release packet is: extension id, display
name, version, archive path, checksum, package-family status, included
surfaces, validation results, install/enable smoke checklist, host handoff
items, rollback owner, and known risks.

Use these release status values:

- `ready`
- `host-coordinated`
- `deferred`
- `blocked`
- `metadata-only`
- `not applicable`

## Readiness table

| Surface | Manifest IDs | Runtime package present? | Host hook bound? | Status | Smoke test | Owner |
|---|---|---:|---:|---|---|---|

## Host handoff status to release status

| Host handoff status | Typical release status |
|---|---|
| `bound-in-production` | `ready` |
| `bound-in-staging` | `host-coordinated` until production-bound |
| `host-hook-requested` | `host-coordinated` |
| `metadata-only` | `metadata-only` |
| `deferred` | `deferred` |
| `blocked` | `blocked` |
| `unsupported` | `blocked` or `not applicable`, depending on whether the feature remains in scope |

Do not launch live behavior while required route, job, native, provider,
database, migration, settings, or content-model host work remains `blocked`,
`deferred`, or `metadata-only`.

Use `not applicable` only for surfaces not included in this extension, not for
included surfaces that are incomplete.

## Operator handoff

- upload location
- install steps
- enable steps
- first screen/page to verify
- expected dashboard cards and admin pages
- expected public pages/page seeds
- expected provider attribution in usage records
- expected site-brain records and logs
- escalation path for blocked dependencies

Sign-off fields:

- extension author
- Cedros host owner
- operator
- QA/reviewer
- install gate status
- launch gate status

Install gate means metadata can be uploaded, reviewed, installed, enabled,
disabled, and removed safely. Launch gate means live behavior can run in the
target environment.

Validation evidence should name the command, environment, timestamp or commit,
and result. Screenshots or manual smoke notes are acceptable only when no
automated command exists.

Supply-chain evidence should name the source repository URL, exact commit SHA,
source directory, license, signed commit/tag check result, SBOM result,
provenance result, artifact signature result, rebuild result, and final archive
checksum. Use one of these machine statuses: `not-provided`,
`provided-unverified`, `source-unreachable`, `source-revision-matched`,
`signed-source-verified`, `provenance-verified`, `sbom-present`,
`artifact-signature-verified`, `rebuild-matched`, `verification-failed`, or
`not-applicable`. Only Cedros marketplace or the operator verifier can mark
evidence verified; authors can only provide evidence.

## Supply-chain verification procedure

Run this procedure when the release targets a marketplace, source-available
distribution, or any channel where users should see trust evidence:

1. Read `supplyChain` from `cedros-extension.manifest.json`. If it is omitted,
   record `status: "not-provided"` unless the release channel requires
   evidence, in which case block the release.
2. Fetch the declared `source.repositoryUrl` over HTTPS and check out the exact
   `source.revision`. Never verify a moving branch name.
3. If `source.directory` is present, run all source checks from that
   repo-relative directory. Reject absolute or parent-directory paths.
4. Verify commit and tag signatures when `commitSigned` or `tagSigned` is true.
   Record pass/fail/not-applicable checks separately for commit and tag.
5. Fetch every declared provenance, SBOM, and signature URL. Compute SHA-256 and
   compare it with the manifest digest before parsing or trusting the payload.
6. Verify provenance with the declared format. For SLSA/in-toto evidence,
   confirm subject digests, builder identity, workflow reference, and source
   revision. For Sigstore bundles, verify identity and transparency-log
   material according to the release policy.
7. If `build.reproducible` is true, run `build.rebuildCommand` in the declared
   environment from a clean checkout and compare produced package artifact
   SHA-256 values with `supplyChain.artifacts[]`.
8. Compute the final upload ZIP SHA-256 after packaging. Keep that digest in
   the release packet or inventory verification record, not inside the manifest
   embedded in the same ZIP.
9. Attach a computed `supplyChainVerification` record to the marketplace
   listing, catalog metadata, or host inventory state. Do not modify
   `cedros-extension.manifest.json` to add verification results.

Computed verification record shape:

```json
{
  "status": "rebuild-matched",
  "summary": "Cedros marketplace verified source revision, provenance, signature, and rebuild evidence.",
  "verifiedAt": "2026-05-05T14:00:00Z",
  "verifiedBy": "cedros-marketplace",
  "sourceRepositoryUrl": "https://github.com/example/extension",
  "sourceRevision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "sourceDirectory": "extensions/example",
  "archiveSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "checks": [
    {
      "checkId": "source-revision",
      "label": "Source revision",
      "status": "passed",
      "summary": "The submitted package source resolves to the manifest revision."
    },
    {
      "checkId": "artifact-digest:server",
      "label": "Server artifact digest",
      "status": "passed",
      "evidenceSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
    }
  ]
}
```

Allowed check statuses are `passed`, `failed`, `warning`, `not-run`, and
`not-applicable`. Use `verification-failed` for the top-level status when any
release-channel-required check fails. Use `provided-unverified` when evidence
exists but no Cedros verifier has run yet.

## Readiness checklist

Manifest/archive:

- archive opens as ZIP
- `cedros-extension.manifest.json` exists
- `cedros-extension.bootstrap.json` exists
- bootstrap manifest deep-equals manifest entry
- manifest passes standalone validation
- package names and versions match `packageFamily`
- fixed registration entrypoints are present
- if `supplyChain` is present, source URL is HTTPS, source revision is a full
  commit SHA, external evidence URLs have SHA-256 digests, and no verification
  result is self-asserted in the manifest

Runtime surfaces:

- admin pages import and render
- installed first paint uses the final labels/groups/order without sidebar
  movement; runtime sections/groups exactly match the persisted manifest
  navigation projection
- dashboard cards import and render
- web/page seed surfaces resolve or are clearly metadata-only
- visual-builder blocks/templates import and render with safe props
- public web blocks include executable Components/renderers for every declared
  public block type that appears in seeded or authored pages
- static first-render shells and browser-running components have documented
  visual parity checks
- every enabled public provider/action control has a real runtime action path or
  is disabled/hidden when required config is absent
- onboarding links resolve to real admin pages
- form starters appear in owner creation workflows; extension docs appear as
  standard draft rows in Docs
- AI skills/capabilities publish discovery metadata
- provider access entries appear in extension metadata
- database access entries reference declared server content models or migrations

Host coordination:

- server route hook ids are bound or explicitly deferred
- wasm `handle-route` handlers route on the **full** request path including the
  `/extensions/{id}` mount prefix — a bare-subpath matcher misses every request
  and returns extension-owned 404s
- if the extension uses `http.fetch`, the operator has configured
  `CEDROS_EXTENSION_HTTP_ALLOWLIST` (JSON map of extensionId → allowed hosts);
  without it all egress fails closed — list the required hosts in the handoff
  packet
- server job executor ids are bound or explicitly deferred
- migrations and settings schemas have an owner
- content models have an owner
- database migrations, retention, disable/remove behavior, and rollback
  compatibility are documented
- native screen/stack/provider ids have an owner
- host-provider services are available where runtime calls need them
- no required official/composable contract is consumed unless its status is
  `published`, or the release packet marks it host-coordinated, deferred, or
  blocked
- third-party webhooks or downstream APIs have credentials stored safely

Supply-chain review:

- source repository URL is reachable or marked `provided-unverified`
- source revision resolves to the submitted release source
- signed commit/tag checks have explicit pass/fail/not-applicable status
- source license is recorded when source is public or source-available
- SBOM is present when the release channel requires it
- provenance or attestation is present when the release channel requires it
- package artifact digests match the produced package artifacts
- final upload ZIP checksum is computed by packaging or intake and kept in the
  release packet, not embedded in the manifest
- no marketplace verification status is authored inside `supplyChain`

Do-not-launch table for unresolved hooks:

| Hook | Status | Owner | Launch impact | Next action |
|---|---|---|---|---|

Operational smoke:

- install succeeds
- enable succeeds
- disable succeeds without data loss
- seeded public routes open after a fresh enable — page seeds are provisioned
  inside the enable request, so a 404 on a seeded route after enable resolves
  is a failure; docs, form, and policy starters provision asynchronously and
  may lag briefly
- static public HTML and the browser-running extension block have the same
  approved visual contract
- each interactive public block performs its core action on the actual public
  route, not only in an isolated component test
- provider/config-dependent controls are absent or disabled when runtime config
  is missing
- re-enable returns to the expected state
- missing dependency shows a useful blocked state
- provider-disabled state fails closed
- important actions emit analytics/site-brain/logging records where the runtime
  seam is published, including Wasm telemetry, or have explicit host handoff rows
  when emission is host-coordinated
- uninstall/remove expectations are documented

Public live-flow smoke:

- **Login/account**: open the seeded login/register route, confirm provider
  buttons match configured feature flags, click each enabled provider, submit
  email/password where enabled, verify expected `/extensions/<id>/*` requests,
  session cookie behavior, loading state, error state, and redirect/account
  state.
- **Commerce/shop**: open a seeded product/listing page, add to cart, change
  quantity, remove line item, reach checkout, create checkout/order handoff,
  and verify empty cart, inventory/unavailable, payment setup missing, success,
  and failure states.
- **Wallet/payment**: verify wallet/provider detection states, connect/sign or
  payment provider handoff, cancellation, retry, and missing-provider behavior.
- **Forms/search/filtering**: submit with valid and invalid data, verify
  validation messages, network request, success state, duplicate/retry behavior,
  and no accidental submission from disabled controls.

Static screenshot review is not a substitute for this live-flow smoke. A public
extension surface is not ready if the first screenshot looks correct but the
browser-running component changes styling, loses accessible labels, or fails to
perform the advertised action.

## Offline runtime bundle gate

Use this gate only when the target environment requires air-gapped or
zero-network server builds. It is separate from the standard
`.cedros-extension.zip` upload. Private and managed extensions still include
compiled admin runtime in that ZIP; do not create a separate npm/tarball
handoff for Cedros Data intake.

Offline runtime bundle handoff must include:

- reason zero-network installation is required
- exact package members covered by the offline bundle
- `Cargo.lock`, `cargo vendor` output, and `.cargo/config.toml` for Rust server
  builds
- package checksums, SBOM/provenance, and source revision
- non-network build command, such as `cargo build --offline`
- validation result from a clean environment with network disabled
- operator storage location and rollback instructions

If the release does not require air-gapped builds, record
`offlineRuntimeBundleStatus: "not required"` and do not vendor dependencies into
the normal local-intake archive.

## Release rules

- Do not mark live server/native behavior ready unless the host binding exists.
- Do not hide metadata-only gaps in release notes.
- Do not ask operators to paste secrets into manifests, logs, or archive files.
- Do not require broad admin permissions when a narrower permission exists.
- Do not call source/provenance evidence "verified" unless Cedros marketplace
  or the operator verifier performed the check.
- Do not skip disable/re-enable smoke for extensions with jobs, providers, or
  public pages.

## Stable id rules

Treat these as durable once released:

- `extensionId`
- `adminModules[].moduleId`
- admin section ids
- admin navigation labels, group merge keys, order, and gating metadata
- dashboard card ids
- block and template ids
- page seed ids and route paths
- form template ids
- docs article ids and slugs
- analytics event ids
- AI skill and capability ids
- provider access ids
- database access ids
- content model ids
- migration ids
- settings schema ids
- server route/job/event ids
- native screen/stack/provider ids
- official extension public contract ids
- composability public contract ids
- consumed capability ids when they affect compatibility

When an id must change, document:

- old id
- new id
- migration behavior
- compatibility shim or fallback
- removal version
- owner-visible impact

## Versioning rules

- Patch version: bug fixes, copy fixes, non-breaking UI improvements,
  non-breaking descriptive changes on existing ids.
- Minor version: new optional surfaces, new dashboard cards, new admin pages,
  new page seeds, new provider access entries, new database access entries, new
  form starters/docs drafts, new AI skills/capabilities that do not break existing
  installs, or adding optional source/provenance evidence without changing
  runtime behavior.

Changing an existing admin page's group, label, order, permission/dependency
gating, replacement, disabled state, or page-access metadata changes persisted
navigation. Ship the manifest and runtime projection together in the same
release, then upgrade/reinstall and enable the extension so the new projection
replaces installed state. Never ship only one side.

- Major version: removed or renamed ids, changed required services,
  incompatible data/schema changes, changed route ownership, changed auth
  requirements, or behavior that can break existing authored content/jobs.

Adding any new contribution id is normally a minor release, even when
optional. Patch metadata additions are limited to non-breaking descriptive
changes on existing ids.

Release classification examples:

- Patch: fix dashboard copy, fix admin loading state, clarify an existing event
  description without adding a new event id.
- Minor: add a dashboard card id, form template, analytics event, content
  block, content template, onboarding page, site-brain record category,
  optional AI skill, optional provider/database access entry, optional page
  seed, or optional official/composable public contract.
- Major: rename a page seed id, remove a block type, change a public route path
  without redirect, require a new mandatory service, add a required official
  extension dependency, tighten a `dependencies.extensions[]` version bound,
  add a required provider/database scope, make auth/permissions stricter, or
  introduce a forward-only migration that prevents safe downgrade, requires
  coordinated host rollout, or breaks old package versions.
- Major: add a new required setting, add a new required secret, change an
  official/composable contract from optional to required, or apply a stricter
  consumed extension version bound.
- Major risk: changing a consumed official/composable contract from optional to
  required, or from draft to assumed-live without published status, is a
  breaking release risk.
- Marketplace review risk: changing source repository URL, package source
  directory, signing identity, builder identity, provenance format, license, or
  reproducible build command can require a fresh trust review even when the
  semantic version is patch or minor.

Migration reversibility categories:

- `no data migration`
- `reversible`
- `forward-only`

## Version, upgrade, and rollback plans

Version plan:

- release type and rationale
- exact version changes across manifest, server, React, and React Native
- compatibility min/max Cedros versions
- `compatibility.minCedrosDataVersion` targets the running Cedros Data host
  release. It is not an npm or Cargo dependency version; pin only the lowest
  host release that provides the runtime seams the extension uses
- stable ids kept
- ids added
- ids deprecated
- ids removed only with migration/rollback notes

Upgrade plan:

- install/upgrade path
- package build/archive order
- Wasm component rollout order and any host hook rollout order
- data migration order
- settings/default changes
- page seed behavior for existing sites
- automation schedule changes
- provider access/scope changes
- supply-chain evidence changes and verification impact
- database access/scope and migration changes
- site-brain and analytics changes
- staging verification matrix

Rollback plan:

- safe rollback version
- rollback trigger conditions
- disable behavior
- data rollback or forward-fix strategy
- job/route/webhook pause strategy
- public page and content fallback
- operator communication
- recovery records/logs to expect

## Upgrade safety checks

- Manifest diff is reviewed for changed ids, required services/capabilities,
  routes, jobs, provider scopes, package versions, and compatibility bounds.
- Package versions match manifest version unless intentionally documented.
  Intake does **not** verify the runtime `package.json` version against
  `packageFamily.react.version` — only the loaded bootstrap's returned version
  is checked (exactly) at load time, so keep the package-version check in the
  author packaging script.
- When the manifest pins `compatibility.witChecksum`, rebuilding the wasm
  component against a new host WIT requires refreshing the checksum, or install
  fails — treat a host WIT bump as an upgrade blocker until the checksum is
  refreshed.
- WIT compatibility is directional. A current host may explicitly admit a
  reviewed older guest checksum; an older host cannot be assumed to implement
  imports from the current WIT. The old-package/new-host and
  new-package/oldest-host support claim applies to browser-only packages unless
  the runtime-protocol matrix explicitly says otherwise.
- Old authored documents still render or have a migration path.
- Existing page seeds are not overwritten unexpectedly.
- Existing dashboard/admin links still resolve or redirect.
- Existing analytics/site-brain event meanings stay stable.
- Existing provider usage attribution remains queryable.
- Existing supply-chain evidence remains reachable, or the release packet
  explains why the trust status changed.
- Existing job schedules are paused, migrated, or left intact intentionally.
- Existing route/webhook callers get a compatibility path or clear failure.
- Existing settings are preserved or migrated with defaults.
- Official/composable provider and consumer version mismatches have degraded
  mode or launch-blocking behavior.
- Consumed official/composable contract status remains `published`, or the
  release is classified as host-coordinated, deferred, or blocked.

## Rollback safety checks

- Disable stops live jobs and provider calls where appropriate.
- Re-enable restores expected owner-visible surfaces.
- Rollback does not orphan public routes or page seeds.
- Rollback does not delete owner-authored content without explicit approval.
- Rollback has a data plan: nothing reverses automatically, because the host
  runs no extension migrations.
- Rollback uses `POST /admin/runtime/wasm-extensions/rollback-reinstall`; a
  plain install of a version older than the latest approved one fails with
  `VersionOutdated`.
- Rollback requires the prior server, React, and React Native package versions
  to remain available to the host.
- Rollback keeps prior source/provenance/SBOM/signature evidence and archive
  checksums available to the marketplace or operator verifier.
- Rollback preserves enough logs/site-brain context for support.
- Host-hook rollout states document whether two versions can be active at the
  same time or whether rollback must be forward-fix only.
- Database changes account for the CedrosCloud blue/green overlap: the previous
  host may keep writing after expansion starts. Constraint validation and data
  backfills are separately retryable, and new writers do not emit values or
  shapes that the active previous host cannot tolerate before cutover.

## Versioning and rollback rules

- Do not reuse an id for a different meaning.
- Do not remove released ids without a deprecation note.
- Do not make a required service/capability stricter in a patch release.
- Do not add provider scopes silently.
- Do not change public route behavior without a compatibility or redirect plan.
- Do not claim downgrade support if data migrations are forward-only.
- Do not replace a release source revision with a branch name or mutable tag.
- Do not silently drop SBOM, provenance, or signature evidence that was present
  in a prior marketplace-approved version.

## Disable, remove, re-enable, rollback matrix

What each surface does across lifecycle transitions:

| Surface | On disable | On re-enable | On remove | On rollback |
|---|---|---|---|---|
| Admin pages | hidden or disabled | restored | removed from extension inventory | previous version restored |
| Dashboard cards | hidden | restored | removed | previous card behavior restored |
| Page seeds | owner-created pages preserved | no overwrite | pages preserved unless explicitly deleted | route/template fallback applies |
| Content blocks | existing content renders or shows recoverable missing-block state | definitions restored | authored content preserved | old renderer or migration needed |
| Content templates | no longer offered for new use | restored | existing pages preserved | previous template restored |
| Form starters | hidden for new creation | restored | existing forms preserved | previous starter metadata restored |
| Extension docs drafts | no new draft provisioned | draft provisioning resumes | existing docs preserved | previous draft metadata restored |
| Onboarding pages | hidden | incomplete steps restored | completion state preserved or removed by policy | previous onboarding metadata restored |
| Analytics events | new emissions stop | emissions resume | historical reports remain queryable | old event meanings preserved |
| Site-brain records | new records stop | records resume | historical records remain | records remain support context |
| AI skills | removed from discovery | restored | removed from discovery | previous docs restored |
| AI capabilities | execution blocked | restored if endpoint exists | removed from discovery/execution | endpoint compatibility required |
| Provider access | calls fail closed | calls resume | access metadata removed | prior scopes restored |
| Database access | route/job calls fail closed or pause | calls resume | data preserved unless policy says delete | migration compatibility required |
| Customer tag access | extension assign/unassign calls fail closed; admins keep using the chip | calls resume; next assign refreshes attribution | tag definitions preserved (admin can delete); assignments preserved | prior scopes/slugs restored |
| Extension dependency | dependent surfaces disabled if required dependency absent | restored when dependency returns | dependency metadata removed | provider/consumer version compatibility required |
| Automation jobs | schedules pause/disable | resume per settings | schedules preserved or removed by policy | executor compatibility required |
| Server routes | unbound or fail closed | rebound | unbound | redirect/compat path required |
| Native screens | hidden/unavailable | restored | unbound | previous hook restored |

## Acceptance checks

- The release packet can be read without private source access.
- Every declared surface has one of the allowed statuses: ready,
  host-coordinated, deferred, blocked, metadata-only, or not applicable.
- Every blocked dependency has an owner and next action.
- Operators can install, enable, verify, disable, and escalate from the packet.
- Rollback behavior is explicit enough to execute during an incident.
- Version changes are consistent across manifest and package metadata.
- Every changed surface is classified as additive, compatible, deprecated, or
  breaking.
- Every breaking change has owner impact, migration, and rollback notes.
- Upgrade and rollback can be tested in staging with concrete steps.
- Operators can tell whether to approve, defer, or coordinate host work before
  installing the update.

Next: [`24-worked-example.md`](24-worked-example.md).
