← Overview

Releasing XGPUI

This is the authoritative release guide. Historical publication evidence is kept in the publication archive; release-specific reviews live in release-reviews/.

This procedure is for the agent to execute. The user requests a release, reviews its proposed versions and notes, and gives the approvals below. The agent runs the commands, monitors workflows and handles failures.

1. Prepare — agent

When the user says “prepare today's release”:

User: review the proposal and approve or revise the version numbers. No commands or manual preparation are required.

2. Merge — agent, after approval

User: explicitly approve merging the release PR.

Agent: verify relevant CI tests, lint and builds pass, then merge the approved PR and remove its branch, or confirm the merge if the user performed it. Record the full merge commit SHA from GitHub. Preserve unrelated local edits; a local checkout/cleanup failure does not mean the remote merge failed.

3. Create the tag and release record — agent

Before any crates.io upload:

4. Publish and verify crates — agent, after approval

User: explicitly approve publication of the stated new versions and any still-pending, individually presented public API changes. Ask plainly: “Publish these N new crate versions? These other versions will be verified and skipped.” Identify any pending API item and its compatibility impact plainly; merging implementation does not itself approve its final public API.

Agent:

On failure: retain the tag and draft, diagnose the failure and retry the same commit where safe, skipping verified uploads. Never move a tag after an upload. A source fix requires a repair PR with explicit merge approval, then a new commit-derived tag/draft and another tagged plan. Preserve the old tag and mark its draft superseded. Before any upload, unchanged candidate versions can be reused if still available; after an upload, do not reuse that version for changed contents. Keep the record in draft and hold docs deployment until verification succeeds.

5. Deploy documentation — agent, after approval

User: approve documentation deployment.

Agent: check that the intended docs source describes the published state, replacing stale “in preparation”/“unpublished” wording while retaining alpha warnings. Any source edits go through a PR and explicit merge approval. Then manually dispatch Pages, monitor it to completion, verify the deployed commit, live site, documented versions, galleries and both Wasm bundles, and add the deployment link to the release record. Report docs deployment separately from crate publication.

Versioning policy

Apply this policy during preparation:

Require a version rationale for every changed crate. Automated Rust API checks should inform this review; they must not assign versions blindly. Until those checks are implemented, the agent performs the review.

Documentation-only publishing

Website fixes and improvements may be published independently, without a crate release, release tag or crate version bump. The agent validates the docs, obtains deployment approval and manually runs Pages.

Keep the website accurate for currently published crates. Do not announce unreleased APIs as available. Record which source commit was deployed; a docs-only deployment is not a new crate-release baseline.

Documentation inside a published crate archive is different: shipping its updated contents requires a new crate version.

Agent responsibility for automation gaps

The agent performs any steps the workflows do not yet automate. The finalizer can create missing tags/records after uploads and replaces the title and notes with generated text. Create the tag/draft beforehand, preserve the reviewed title/body outside the checkout, and restore them with verification evidence before publishing the record. Updating this guide does not change workflow behavior.

Mark abandoned partial releases incomplete or superseded, preserving their source tags and publication results. Never use them as completed baselines.

Always use the most recent completed release in the approved source history as the preparation baseline, including an earlier release from the same day. Exclude unfinished release tags. Ask the user only for a missing decision, required approval or a blocker the agent cannot resolve.

Local validation before review

Run from the repository root using the existing commands:

just test
just pages-check
git diff --check

Release tooling and its regression tests live in tools/release-sync and run through just test on every host, without Python. To run only that suite:

just test --only release-sync

Registry checks are a separate read-only network operation:

just release-check --epoch LAST_COMPLETED_RELEASE_TAG

just release-check checks changed packages, versions and dependency pins against release history and crates.io; it is read-only but requires registry access. Pass the completed baseline explicitly: the history parser currently recognizes only the verification table format below and can miss releases recorded only with prose or old/new version tables. A missed baseline can falsely flag unchanged crates. Confirm the actual tag and packaged diff before proposing bumps.

It does not compile or test the code. just test runs the current host's suites, including native-library and benchmark compilation. Use just test --list to inspect coverage or just test --only native-libraries to isolate a failure. Prerequisite failures must be resolved or recorded as outstanding gates.

Run native validation on macOS, Windows and Linux for changes affecting those platforms. A successful Mac run or cross-check does not establish native Windows or Linux behavior. Use the relevant lab recipes in Building and Local development for graphical acceptance; for the 1.27.0 integration, follow its ordered lab checklist. Record browser, iOS and optional-feature checks where the release affects them. CI results supplement this evidence; preparation workflows do not establish that all native or graphical validation passed.

just pages-check checks the static website in pages/; update its pages by hand, including the upstream baseline on the overview after a sync. Toolchain and host prerequisites are documented in Upstream release imports and Building.

Preparation and publication tools

Work in the normal repository checkout. Keep builds and downloaded workflow reports under target/. Use a named, persistent release-artifacts directory outside the checkout for isolated consumers and tools that require external outputs; do not create /tmp checkouts or scatter release files there. The agent creates that directory and sets XGPUI_RELEASE_ARTIFACTS to its absolute path before using the examples below. Keep edits local until the user requests a branch push or PR.

The version source of truth is scripts/release-packages.json. Do not maintain a second current-version table in this guide. Release notes carry the reviewed old → new versions and a rationale for each changed crate.

The Prepare daily release PR workflow runs on main. Supply the most recent completed ancestor release explicitly as its baseline: its automatic tag selection does not exclude unfinished releases. It proposes versions, refreshes lockfiles, checks release tooling and opens a PR. Its automatic version proposals still require the compatibility review above. For local preparation, choose new report paths outside the checkout:

cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- daily prepare --baseline APPROVED_COMPLETED_RELEASE_TAG \
  --report "$XGPUI_RELEASE_ARTIFACTS/preparation.json" --notes "$XGPUI_RELEASE_ARTIFACTS/notes.md"

This previews automatic patch suggestions; it is not needed when versions are already prepared. Apply only the user-approved version plan, preserving compatible requirements. After version changes, run cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- daily refresh-locks and review the resulting diff. Reject unplanned external dependency changes.

The Prepare registry release workflow checks out the selected dispatch commit and uses the release map. Local no-upload equivalents, each with a new output directory outside the checkout:

cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- registry-release --mode plan --out "$XGPUI_RELEASE_ARTIFACTS/plan"
cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- registry-release --mode dry-run --out "$XGPUI_RELEASE_ARTIFACTS/dry-run"

Plan and dry-run have no uploads and need no upload credentials, but access crates.io and may download dependencies. Existing versions are skipped only after comparing packaged source and normalized manifests. Generated lockfiles, VCS metadata and publication permissions are excluded from that comparison; source, resources, README, features and dependency requirements remain significant.

Each upload is checked through the registry API and a checksum-verified download before dependents proceed. Reports, logs and archives are retained in workflow artifacts, including failures. A retry checks registry contents again and skips matching published versions. Unknown upload outcomes stop; investigate before retrying. Never change an uploaded version's source or move its release tag. Complete the registry-only consumer verification before declaring success.

The obsolete six-extension 0.2.0 publisher has been removed. Historical publication evidence remains in the archive. Use xgpui-release preflight and prepare-registry for manifest inventory and offline archive inspection; verify-packages and verify-color run isolated archive/consumer checks. Archive generation with --no-verify --exclude-lockfile is not registry-backed build verification. Release commands require Cargo and Git; registry requests also require curl, and GitHub context/finalization requires the gh CLI.

Workflow limitations and release records

The registry workflow invokes xgpui-release daily finalize after successful uploads. It validates the report and tag identity, but overwrites the release title/body and does not publish an existing draft. Agent restoration and explicit draft publication remain required.

Preserve the human release notes and old/new table. Append a separate three-column table containing every mapped crate and its actual report result:

| Crate | Version | Result |
| --- | --- | --- |
| `crate-name` | VERSION | published-verified |
| `unchanged-crate` | VERSION | already-published |

The history checker recognizes exactly published-verified and already-published in the third column. A prose statement or a previous/candidate version table alone is not sufficient. Keep this verification table alongside the highlights so the next release can identify the completed baseline.

The maintenance/Parley release is an example of this sequence: pre-upload tag and draft, 15 verified new versions, 2 verified skips, a registry-only consumer, and restored full notes with the verification table. Pages remained a separate action.

The Publish documentation workflow is manually dispatched. After deployment approval, select the intended source and monitor its build and deployment. Record the deployed commit and deployment link. Docs-only deployments follow the documentation-only policy above.

Upstream release imports

gpui-unofficial is xgpui's upstream. main contains xgpui's maintained code. Sync prepares an incoming tree proposal, not an integrated or buildable update.

Review a release

Run Actions → Sync GPUI release on main with a release number and optional source SHA. Sync normalizes the release's crate names, compares that tree with xgpui's current tree, and opens a new draft PR on a fresh version-named branch. Existing PRs are never edited, force-pushed or merged.

The proposal uses separate commits:

  1. Code: changed source, shaders, build scripts, assets and other package files, including additions and deletions.
  2. Cargo versions/dependencies: package versions and dependency requirements, which affect dependency resolution.
  3. Generated metadata/artifacts: generated package READMEs, provenance and other retained capture metadata.

Release context uses the exact old/new Zed SHAs in snapshot provenance, never latest-release or previous-stable guesses. Its path filter is the union of both captures' transform-metadata.json crate inventories, including nested crates. It links relevant Zed commits and referenced PRs, with root Cargo dependency changes and unrelated range commits labeled separately. Missing provenance, incomplete API pagination and non-forward ranges fail context generation rather than silently reporting no changes. Markdown/JSON context accompanies the PR and workflow artifact; no issue or comment is created in the upstream repository.

The PR links each commit and lists code differences separately. The detailed JSON comparison is uploaded as a workflow artifact. A release with no differences creates no PR.

Sync also compares a clean snapshot of the previously recorded source release. This labels whether each proposed file changed upstream and whether xgpui has a local modification. It does not automatically merge or resolve those overlaps. A downstream-only code difference is reported and retained, not applied. When upstream also changed a file, its incoming version is a manual review proposal: combine or reject overlapping changes before any merge.

main remains untouched. Complete incoming versions are placed only on the review branch. Platform validation, integration, publishing and merging are separate work.

For example, 1.25.3 contains release metadata changes and no upstream Rust source changes relative to 1.25.2. The spring clock difference against xgpui is a local patch, not an upstream fix; it is retained with its regression assertion.

Names and provenance

For example, gpui-unofficial becomes gpui, and gpui-linux-gpui-unofficial becomes gpui-linux. Rust dependency aliases such as gpui_linux stay as they were. Cargo package identities, dependency references, directory paths and local overrides change together. The unused nested transformed-crates staging mirror is excluded from imports and removed by normalization. Required per-crate vendored sources are preserved.

upstream.json retains the source repository, release and commit, plus Zed's reported provenance. source_packages retains original names/paths; packages describes the local normalized tree. normalization records the schema and name map. registry_packages records the supporting crates fetched from the matching published gpui-unofficial release. Retained local crates preserve upstream repository URLs, Rust source, licenses and READMEs.

Package names change for downstream consumers. Existing dependency aliases can stay, but their package and path fields must point at the normalized names. Local directory names remain stable. scripts/release-packages.json maps the maintained packages to downstream xgpui-* identities at 1.0.0 for the framework and 0.2.0 for extensions; upstream.json records those separately under distribution. Upstream helper versions continue to follow the captured release.

Integrate and validate

Review and resolve the incoming proposal on a development/integration branch. Preserve local Rust edits when resolving conflicts; do not replace main's capture with a fresh generated snapshot as a substitute for merging. The first normalization is a directory/package migration; later releases use the same stable paths.

After integration, run from the repository root. The local-source patches live in the root workspace manifest, so the standalone tools' lockfiles don't record them and need no refresh when captured versions change:

cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- normalize --check
cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- build-manifests
cargo check
cargo build --locked

cargo test --locked --manifest-path tools/release-sync/Cargo.toml
cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- build-manifests --check

build-manifests reads the package map and preserves native target selection, standalone workspace exclusions and the compatibility patches. It supersedes the removed Python update_build.py compatibility entry point. Review and retain the root lockfile.

Supporting crates listed in scripts/registry-crates.json resolve from crates.io at the captured release version. Only the retained framework/backend crates are locally overridden; see build details.

For standalone checks, continue invoking Cargo from the repository root so it discovers .cargo/config.toml (the shared target directory). The captured crates are root workspace members, so the root manifest's local-source patches apply. For example, on macOS:

cargo check --config .cargo/validation.toml \
  --manifest-path gpui/crates/gpui-platform/Cargo.toml \
  --features font-kit,runtime_shaders
cargo test --config .cargo/validation.toml \
  --manifest-path gpui/crates/gpui/Cargo.toml --lib \
  --features gpui_platform/runtime_shaders

.cargo/validation.toml remains separate from generated capture overrides. It restores the Proptest and async-process Git revisions needed by this release. Cargo patches do not force a lower version over a newer compatible registry version; inspect metadata when updating versions. A separate consumer must carry its own local package overrides rather than assuming this checkout's config is inherited.

Review the website in pages/ after each release; just pages-check catches broken links and a stale upstream baseline, not stale narrative. See website maintenance and build options.

Sync performs no platform validation. Its branch is an unintegrated proposal; review and test the chosen integration separately.

GitHub settings

Keep main as the default branch. Enable Allow GitHub Actions to create and approve pull requests in repository/organization Actions settings. Sync creates only new draft PRs; it never approves or merges them. The old persistent upstream and upstream-normalized branches are not used or updated.

How imports and builds stay separate

The captured manifests each declare their own workspace. The root workspace explicitly excludes each of those directories and builds their libraries as path dependencies. The harness does not rewrite their manifests or source. A separate downstream spring-animation fix uses the scheduler clock so headless tests advance deterministically; the clean upstream branch retains the original. The unused nested transformed-crates staging mirror is removed during import and normalization. Required per-crate vendored shader inputs remain intact.

Cargo.toml and tools/build-all/Cargo.toml are generated development files; they are not overwritten by upstream imports or downstream normalization. After an import adds or removes packages, regenerate them and update the root lockfile on the development or integration branch:

cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- build-manifests
cargo generate-lockfile
cargo build --locked

CI checks xgpui-release build-manifests --check to detect a stale harness. cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- build-manifests --check delegates to the same Rust generator. Review its platform classification when upstream adds a backend. Keep the root Cargo.lock committed for reproducible native builds.

The generated root manifest restores production compatibility dependencies from .cargo/validation.toml. The Proptest override stays scoped to explicit standalone test validation, avoiding an unused-patch warning in root library builds. Compiling the macOS utility crate requires Zed's async-process fork: its process launcher calls Child::adopt_raw_pid, which the registry dependency does not provide. The fork revision is taken from the original Zed release's workspace patch. These overrides repair extraction omissions without editing captured source.

The local Apple backends and extension producers use objc2, objc2-metal, and block2. The legacy Cocoa/metal-rs block compatibility patch is no longer needed by this checkout. Published versions can still resolve the older bindings; verify a consumer's selected dependency graph independently of local builds.

Local naming

The raw gpui-unofficial release remains on upstream. Development uses a normalized capture with names such as gpui and gpui-linux. The Rust tool in tools/release-sync applies scripts/crate-names.json and preserves original package paths in upstream.json. See the sync process.

Published supporting dependencies

scripts/registry-crates.json maps former local helper names to their published *-gpui-unofficial packages. Normalization preserves Rust dependency aliases, features, target conditions, and upstream source provenance. It removes helper source directories and local patches, and records registry identities and the release version in upstream.json. Core, macros, and platform/renderer backends remain local. Runtime Rust source is not rewritten by this migration.

After an upstream import, run normalization before generating build manifests. Normalization also updates direct helper dependencies in app and extension manifests. Refresh their lockfiles when changing the captured release. The registry version follows the captured core manifest, not the latest registry release. Keep shared types such as Refineable on the same registry package throughout the graph.

Verify resolved package identities from the repository root:

cargo metadata --locked --format-version 1 > /tmp/xgpui-metadata.json
cargo run --locked --manifest-path tools/release-sync/Cargo.toml -- check-local-dependencies --metadata /tmp/xgpui-metadata.json

This checks local framework sources, pinned registry helpers, and accidental pulls of the upstream framework alongside the local fork.

Downstream release identities

scripts/release-packages.json defines downstream package identities and individual version overrides for ten framework/backend crates and seven extensions. Normalization applies those versions while keeping directory names, library names and dependency aliases stable. The helper package version follows the captured release/1.27.0 provenance independently. upstream.json records both sets.

For example, the local gpui crate is packaged as xgpui; applications retain use gpui::* through a Cargo alias. Internal dependencies use exact requirements from the release map. Supporting packages retain their upstream identities and =1.27.0 requirements. The selected downstream family uses core 3.0.0 and Apple/macOS 4.0.0, with matching exact pins. The integration still requires final public API review before release. Interactive iOS simulator/device validation remains pending.

Website deployment

.github/workflows/pages.yml runs on manual dispatch only. It checks pages/, builds both demo collections, checks that the bundles exist and uploads pages/ as is. Internal documentation and tooling are never uploaded.

Select Settings → Pages → Source: GitHub Actions. After deployment, repeat the browser checks at https://xgpui.dev/ and refresh nested URLs directly to catch stale assets.

Custom domain

Production traffic for xgpui.dev was migrated in issue #61 and PR #64 (verified 2026-10-28).

dig A xgpui.dev +short
dig CNAME www.xgpui.dev +short
dig TXT _github-pages-challenge-gpui-luma-dev.xgpui.dev +short
curl -IL https://www.xgpui.dev/
curl -IL 'https://gpui-luma-dev.github.io/xgpui/examples/extensions/?demo=color&backend=webgl'

For rollback, redeploy a known-good commit through the workflow. If removing the domain mapping, change Pages DNS before removing the GitHub custom-domain association, and keep the ownership record. GitHub's custom domain and troubleshooting guides have the details.