| title | Workspace release process |
|---|---|
| kind | runbook |
| status | active |
| owner | workspace-maintainers |
| last_reviewed | 2026-08-13 |
| review_by | 2026-11-11 |
| supersedes |
The repository uses independent package versions and immutable Git-only workspace releases. It is a virtual Cargo workspace, not one Cargo package, so there is deliberately no single SemVer value for the repository.
Three identities have different purposes:
- A development snapshot is one full 40-character commit SHA on
main. It is not a release merely because it is reachable. - A package release uses the package's own SemVer and a qualified tag such
as
graphql-orm-ai-v0.73.0. A package tag never moves. - A workspace release is a tested package set named
workspace-YYYY.MM.DD.N, for exampleworkspace-2026.08.11.1. Its attached manifest binds the source SHA, lockfile, every package source tree and version, package tags, external Git revisions, and durable wire/schema contract versions.
Consumers must use the workspace release's full commit SHA in Cargo rev.
Tags improve discovery, comparison, and support but are not authority for a
dependency update.
Packages advance independently because they have different public contracts
and release cadence. graphql-orm and graphql-orm-macros remain aligned
because generated code and runtime support are one compatibility boundary.
A package version changes when its public Rust API, generated code, Cargo features, wire contract, runtime behavior, or documented compatibility changes under that package's release rules. A workspace release does not require a new version for an unchanged package.
graphql-orm-ai additionally versions its persistent schema module. Router,
semantic-catalogue, tool-manifest, and operation-assurance protocols keep their
own contract versions. The generated release manifest records these values
separately from package SemVer. Contract rows are unique and name-sorted so the
same source commit and release ID always produce byte-identical output.
The workspace remains deliberately unpublished on crates.io. Every member
sets publish = false, and scripts/check-release-state.py enforces that
boundary. Registry publication would be a separate distribution project, not
a side effect of this process.
-
Start from a clean branch based on current
main. -
Identify every changed package and direct workspace dependant.
-
Classify public API, GraphQL SDL, persistence, configuration, security, backup/restore, provider, and operational effects.
-
Update affected package versions,
CHANGELOG.md,MIGRATION.md, README, and examples according to the package-localAGENTS.md. -
Move completed work out of
docs/plans/active/; an active plan must describe genuine remaining implementation rather than release chronology. -
Regenerate the package inventory after manifest changes:
python3 scripts/generate-workspace-inventory.py
-
Run the local release metadata gates:
python3 scripts/check-documentation.py python3 scripts/generate-workspace-inventory.py --check python3 scripts/check-release-state.py scripts/check-workspace-dependencies.sh scripts/check-package-release-policy.sh <merge-base-or-reviewed-base-sha> scripts/check-semver.sh <merge-base-or-reviewed-base-sha> scripts/check-release-manifest.sh python3 scripts/test-router-notices.py cargo fmt --all -- --check
-
Run every package, backend, provider, Clippy, Rustdoc, SemVer, migration, restore, and release-policy lane required by the root and package-local instructions.
scripts/check-ai-provider-lanes.shexercises each provider separately, andscripts/run-owned-database-lanes.shsupplies the disposable database evidence. Never use workspace--all-features; database backends are alternative profiles. Local command output is the acceptance evidence; hosted workflow success alone is insufficient. -
Review the complete diff, dependency trees, generated schema/manifest changes, documentation links, and migration statements.
-
In the pull request, select exactly one documentation-impact option from the repository template. Release changes normally select
Documentation updated; CI rejects missing or ambiguous declarations. -
Merge and push the reviewed release commit to
main. Do not tag it yet.
The generator is deterministic for one release ID and commit:
python3 scripts/generate-release-manifest.py \
--release-id workspace-2026.08.11.1 \
--ref 0123456789abcdef0123456789abcdef01234567 \
--check-clean \
--verify-tags \
--output /tmp/workspace-release.json \
--notes-output /tmp/workspace-release.md--verify-tags permits an existing package tag only when that tag's package
source tree is byte-identical to the selected commit. It therefore catches a
package change that reused an already released version.
Run Workspace release manually and supply:
release_id: the newworkspace-YYYY.MM.DD.Nidentity;target_ref: the full commit SHA, which must equal currentmain;prerelease: whether the workspace release is a candidate;include_router_artifact: normally false for source-only releases; androuter_distribution_approval: required when a router binary is attached.
Configure Dastari as a required human reviewer on the release environment
and restrict it to main before dispatch. The guard rejects an environment
without that reviewer; naming an environment in YAML does not create protection.
The protected release environment is the human authorization boundary and
gates the workflow's entry job, so no release lane runs before approval. The
workflow then:
- proves the requested commit is current
mainand the workspace tag is new; - reruns documentation, dependency, package, backend, provider, Clippy, and
Rustdoc release lanes with the lockfile fixed, as parallel jobs that each
own their
target/, one per AI provider lane, all of which must succeed before anything is tagged or published; - generates the canonical JSON manifest and Markdown release notes;
- optionally builds the approved Linux router executable and its CycloneDX inventory;
- hashes and attests every release asset;
- creates package-qualified annotated tags only for versions not already tagged, plus the annotated workspace tag, in one atomic push; and
- publishes the GitHub Release from the existing workspace tag.
Enable GitHub immutable releases for the repository. Prepare every required asset before publication because neither a release tag nor an attached asset may be replaced after publication.
Source and compiled-router distribution are distinct approvals. A router binary may be selected only after the exact target, features, lockfile, linked/native components, advisories, licenses, notices, SBOM, hashes, and delivery channel have a designated approval under ADR-0008.
The binary lane is therefore opt-in and requires an evidence reference. It
builds the explicit auth-agql feature profile for
x86_64-unknown-linux-gnu, packages the binary with the workspace license,
CycloneDX inventory, third-party notice/source evidence, and approval reference,
and includes the archive in the release checksums and provenance attestation.
A later target or feature set requires its own approval.
scripts/generate-router-notices.py collects packaged and nested/native notice
files and supplemental upstream notices whose URLs and SHA-256 values were
reviewed at recorded source commits. It also includes exact registry source
archives for MPL components and the explicitly recorded packages whose
standalone notice files were unavailable. The archives must match Cargo.lock
checksums. The generated inventory preserves original license expressions,
source URLs, file hashes, and source-only notice dispositions; it contains no
builder-local source paths. The bundle also retains the matching Rust standard-library
copyright notices shipped with the matching Rust compiler installation.
config/router-notice-review.v1.json binds these notice-source exceptions to
the lockfile hash, SBOM component set, target, and features. A dependency/profile
change requires a fresh review of those bindings and supplemental sources;
generation fails if the existing record does not match. This configuration is
technical evidence, not distribution approval. The designated owner must
review source-only notice dispositions, legacy license expressions, applicable
source obligations, and the actual delivery contents before providing the
workflow's router_distribution_approval reference. Do not treat a missing
notice as an automatically approved exception.
External agql-auth dependencies may select a published v<version> release tag.
The manifest keeps its full locked commit in externalGitDependencies[].revision
and additionally records tag; all other external Git dependencies still require
full revision pins. Generation fails if the lockfile has no unique matching source
or disagrees with an explicit revision. Verify the auth release's attested manifest
and peeled tag before adopting it. Consumers seeking one Cargo source must use the
same tag selector: a rev and a tag remain different sources even at the same SHA.
Pure Rust libraries do not receive optimized binary artifacts. Downstream Cargo builds compile them from the pinned Git source.
- Before tags are pushed, repair the release commit and rerun the workflow.
- If validation fails, do not publish a partial package/dependency set.
- Package and workspace tags are immutable. Never force-push, move, reuse, or delete a published release identity.
- If tagged source is defective, make a new commit, advance every affected package version, and publish a new workspace release.
- If tag creation succeeds but GitHub Release publication fails, retain the immutable tags, inspect the failed run, and attach the already attested assets to a release for that exact tag. Do not regenerate from another SHA.
- Consumers roll forward to a newly reviewed full SHA; published Git history is never rewritten.