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”:
- Fetch current
mainand identify the last successfully completed release. - Compare code, packaged documentation, dependencies and publication changes.
- Reuse already prepared version numbers and existing release PRs; do not run an automatic bump over them. Propose only the remaining old → new versions, with a reason for each.
- Reuse the existing release prose in docs for the GitHub release notes, retaining change titles, short summaries, links and an old → new version table. Keep alpha limits explicit. Do not rewrite docs merely to create the GitHub record.
- For a first publication, check the crate is in the release map, enable crates.io publication, and verify package metadata, README and the actual
LICENSE-APACHEfile. A manifest license declaration alone is insufficient. Inspect the candidate archive before tagging; a package file listing is a quick check, not full archive/build verification. Raise internal dependency minimums when the new crate uses newly added APIs. - Review automatic version proposals against the versioning policy below. Include a reason for every bump in the PR.
- Run the validation and no-upload registry checks below; resolve failures and version conflicts. Record source identity, host, commands and results in the release review. Revalidate affected checks after source changes.
- For each public API addition or change in non-extension GPUI crates, document purpose, source/serialization compatibility, migration requirements and whether it could live in an extension. Present each concrete API change for final user review; implementation approval does not approve the final public API.
- Present the PR, versions, validation scope and exact publish/skip counts. Distinguish a version audit, archive payload plan and actual package/build verification.
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:
- Create the remote Git tag explicitly, then verify its full SHA before creating the draft. Creating a draft release with a tag name does not necessarily create the Git ref; workflow dispatch requires the real ref.
- Tag the approved commit as
release-YYYY-MM-DD-COMMIT, using its UTC commit date and first 12 hex characters. - Support multiple releases per day: each distinct approved commit gets its own tag, for example
release-2026-10-31-ed4469267251andrelease-2026-10-31-a1b2c3d4e5f6. Retries of the same release reuse its tag and record; do not create a new release identity for a retry. - Create a draft GitHub release containing the reviewed notes and version table. Preserve a copy of its title and body in the named release-artifacts directory before running publication; the current finalizer overwrites them.
- Verify the remote tag resolves to the approved full SHA and the record is a draft. Explain if the UTC tag date differs from the user's local date. Run the final no-upload plan against this tag, download its report and check the exact source SHA, publish/skip counts and payload conflicts. A successful plan has uploads disabled and may still report
release_ready: false; only completed publication verification must report true. Present the draft and results to the user.
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:
- Dispatch publication against the tag, confirming
publish FULL_COMMIT_SHA; do not select a movingmainbranch. Publish in dependency order and monitor through completion. - Verify and skip matching versions already published; verify each new upload.
- Inspect the report: the source SHA must match,
release_readymust be true, and every candidate must bepublished-verifiedoralready-publishedwith a registry checksum. Report the actual new/skip counts. - Use a fresh consumer outside the checkout, with exact versions for the full release map and no repository patches or credentials. Verify its Cargo metadata resolves those versions from crates.io, then build/run representative APIs, including new extension APIs. Use the actual Rust library name, which may differ from the package name (
xgpuiexposesgpui), and exercise new behavior rather than only importing types. State the host and limits; this is not interactive or cross-platform acceptance. - Restore the reviewed title and notes after the workflow finalizer runs. Add the publication/consumer results and workflow link. Keep the version history and add the machine-readable verification table described below. Explicitly publish the existing draft and recheck the tag SHA and published state.
- Report crate publication and release-record completion; say separately that Pages is still undeployed.
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:
- Patch: compatible fixes, internal backend changes, packaged documentation and compatible dependency updates.
- Minor: new capabilities that preserve supported public APIs.
- Major: deliberate breaking changes to supported public APIs.
- Before 1.0: use a minor bump for breaking changes or new capabilities; use a patch for compatible fixes, internal changes and packaged docs. State whether a minor release contains breaking changes.
- Dependent crates: bump only when packaged content or dependency requirements change. Do not automatically propagate a major bump; review whether changed dependency types affect the crate's public API.
- Internal requirements: use full stable compatible requirements (for example,
3.0.0or0.4.0) for XGPUI libraries. Preserve an existing minimum when it admits the candidate version; raise it when code needs a newer API or fix. Normalization and release preparation must not raise compatible minimums just because a dependency releases. Exact requirements remain appropriate in registry verification fixtures. Upstream helper pins remain unchanged. Release tools support full stable caret/default, tilde and exact requirements; other syntax requires explicit review. - Compatibility review: inspect removed or renamed APIs, signatures, exhaustive enum variants, features and minimum supported Rust version. Explain their impact and the proposed version in the PR.
- Exceptions: obtain explicit user approval and record the compatibility impact in the release notes.
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 inspects candidate archives and registry versions, reporting
publish-needed,already-publishedorversion-conflict. Changed packaged contents under an existing version fail the plan. - dry-run additionally performs registry-backed package/build verification where dependencies are available. Dependents of missing candidate versions remain blocked until prerequisites are published; they are not verified yet.
- publish, after approval, requires
--confirmation 'publish FULL_COMMIT_SHA'andCARGO_REGISTRY_TOKEN. It preflights all candidates and verifies/builds and uploads missing versions in dependency order. Use the approved immutable source.
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:
- Code: changed source, shaders, build scripts, assets and other package files, including additions and deletions.
- Cargo versions/dependencies: package versions and dependency requirements, which affect dependency resolution.
- 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).
- The repository Pages custom domain is
xgpui.dev, with Enforce HTTPS. The certificate coversxgpui.devandwww.xgpui.dev. - Organization ownership is verified by the TXT record at
_github-pages-challenge-gpui-luma-dev.xgpui.dev. Retain it. - Apex A records:
185.199.108.153,185.199.109.153,185.199.110.153,185.199.111.153.wwwis a CNAME togpui-luma-dev.github.io. Preserve unrelated DNS records. - No
CNAMEfile is needed: Actions publishing uses the repository setting. http://xgpui.dev/,https://www.xgpui.dev/and the former project URLhttps://gpui-luma-dev.github.io/xgpui/…redirect to the HTTPS apex, keeping path and query. Keep site links relative so localhost previews also work.
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.