Repository navigation
Release Publication Stages
Audience: release maintainers and qualification owners.
Thread context: stable-v1 publication and the repository readiness contract.
Platform/backend scope: Windows 11 x64, MSVC v143, D3D11 and NullRHI.
Publication uses python3 tools/site-data/validate.py --require-candidate-ready.
The final --require-ready check still requires all work and gates complete;
it is not the entry condition for publishing the evidence needed to finish.
The profile's publicationFinalization object declares the terminal work IDs,
their required terminal gate, and the stable-release environment. Those IDs
must exactly match applicable work explicitly typed with
completionPhase: publication-finalization. This is a closed metadata schema,
not an arbitrary exemption list. Technical work cannot depend on a finalizer.
Before publication, all other blocking items and transitive dependencies must
be done, with no planned verification left. All required technical gates must
pass. Gates that also await a finalizer must be at-risk, with their other work
complete; they cannot be described as passing. Finalizers must stay in-progress.
REL-190 owns supported-host rehearsal, migration/rollback/recovery drills,
qualification sign-off, the profile-required-gates and release-approval jobs,
and their planned rehearsal selectors. It must be done before candidate
publication. REL-200 depends on it and owns only publish/download/site
finalization, with no deferred technical job or rehearsal selector. A finalizer
carrying planned verification is rejected.
Every profile and global state stays candidate, with an assigned owner and
reviewed qualification sign-off. Nothing in this procedure promotes the current
blocked ledger or substitutes fixture tests for real qualification evidence.
validate.py also enforces these rules on every run. A capability can be
ready only if it names at least one required gate and has evidence. A gate can
be passing only if it has evidence. Every work-item ID (such as RDY-000) and
gate ID (such as G00) named in contract text must be declared, including
rationale, summaries, limitations and website copy. Every entryPoints,
documentationUpdates and sourceContext path of a work item, open or done,
and every docs-catalog path must exist on disk. A planned output is referenced
only after it lands; tests and CI jobs that do not exist yet are declared in
plannedTestSelectors and plannedCiJobs instead.
Work-item commands are resolved against CMakePresets.json, using the
inheritance resolver in Tools/buildmatrix/inventory.py. cmake --preset <name>
must name a configure preset, cmake --build --preset <name> a build preset, and
ctest --preset <name> a test preset. Every build/<dir> tree passed to cmake or
ctest must be a configure preset's binaryDir. A ctest run must target a preset
that builds tests. A preset whose resolved BUILD_TESTS is false, such as
windows-shipping or linux-shipping, is accepted only when the same item
configures that preset with -DBUILD_TESTS=ON (as RDY-010 and PLT-200 do).
Otherwise the run belongs on a validation preset such as windows-release
(-C Release), linux-gcc-release, or ci-linux-asan. A preset an item will
add is declared in PLANNED_CMAKE_PRESETS in validate.py and keyed to its
owning item. Only that owner's cmake commands may name it, and never a ctest
tree. The entry is an error once the preset exists, the owner is done, or the
owner stops naming it.
Hand-written counts are governed too. validate.py uses the publisher's document
discovery to scan the catalog's root documents and recursive Markdown roots.
The same registry always includes Markdown under wiki/, docs/readiness/, and
docs/status/, even when the publication catalog omits them. It checks a number
followed (within two words) by tests, files, panels, modules, subsystems, backends,
lines, nodes, capabilities, features, components, systems, shaders, commands,
pages, gates, work items or criteria (including singular forms). N+, ~N,
N/M and phrases wrapped across lines are checked; product versions and patch
identifiers are not inventory totals. <!-- AUTO:* --> blocks, fenced code,
blockquotes explicitly labelled Acceptance criteria: or Ledger note:,
generated reference pages under wiki/reference/ and docs/api/, the fully
generated wiki/advanced/Codebase-Statistics.md, and the
sed_replace patterns of docs/update-readme-badges.sh are generator-owned and
skipped. Each pattern is skipped only on the file(s) its own sed_replace call
rewrites (the validator resolves $readme, loop variables, and arrays; an
unresolvable target fails validation), so N specialized panels is managed on
README.md but must be claimed on wiki/getting-started/FAQ.md. Every other hit
must lie wholly inside the text of a readiness.publicNumericClaims entry for
that surface; an entry N nodes does not cover ~N nodes, 1/N nodes or
#N nodes:
classification |
Extra field | Check |
|---|---|---|
metric |
metricId |
Exactly one claim, compared with the value generate.py measures from source; N+ passes while the metric is at least N, and ~N or N/M is refused |
static-fact |
evidencePath |
The cited path must exist (design constants such as the 4096-node mod JSON budget) |
historical |
none | A dated audit or changelog record that is not re-measured |
An entry whose text no longer occurs, or holds no claim, is an error, so rewording a page retires its entry in the same change. Prefer removing per-file line counts over registering them: they drift with every edit.
Generated publication is checked against its producer: the readiness handoff
must equal render_handoff.py output, and generated API pages must pass the
API manifest integrity check. The API output declared by the docs manifest must
match the site-data producer's destination. Publication regenerates the API corpus
and verifies its source metadata before bundling it. These checks govern generated
counts and quoted acceptance criteria; they do not authorize new prose claims.
The architecture flowchart's panel-header inventory is bound to
editor.panelHeaders. The separate editor.panels metric counts factory
registrations; they describe different inventories. Profile-scope and forbidden
readiness wording use the same discovery and prose exemptions, without requiring
every page to discuss a release profile. Ordinary blockquotes remain checked.
These vocabulary-based checks do not resolve arbitrary status labels to capability
entries; RDY-000 tracks that remaining gap. The claim audit
records the expanded scan's findings, classifications, and prose corrections.
CTest runs the whole contract suite as site-data-contract. The faster
readiness-cross-references runs the strict live validation plus the dependency,
promotion, selector, future-path, prose-reference and handoff cases. Both carry
the readiness and site-data labels and are registered on non-Windows hosts
only; the Linux site-data workflow is the suite's CI home.
The publication entry point, tools/site-data/generate.py, invokes the same
Python API producer as docs/generate-api-docs.sh before checking documentation.
This materializes the ignored docs/api corpus on a clean checkout without
requiring a shell for API generation. It then validates the exact-source API
manifest, documentation routes, links and anchors before replacing an existing
publication. Broken links and route collisions fail publication; missing API
pages are never excused by the link validator.
site-data-publisher-docs registers regression checks for that ordering and
failure boundary. Local generation with --allow-dirty --skip-doc-health produces
a blocked development bundle. It is not publication evidence and cannot establish
live-site adoption or an exact-commit CI result.
The consumer lives in tools/site-data/runtime. It has no README of its own: a
Markdown file under the lowercase tools/ tree collides with the published
Tools/ documentation root on case-insensitive checkouts.
siteDataRuntime.mjs is the repository runtime's display boundary for the
generated site-data publication. Construct it with the createFetchLoader
export from verifyBundle.mjs. Display the verified commit from the runtime;
an optional expected-SHA pin is for fixed snapshots, not the moving site:
import { createFetchLoader } from './verifyBundle.mjs';
import { mountSiteDataRuntime } from './browserRuntime.mjs';
import { SiteDataRuntime } from './siteDataRuntime.mjs';
const runtime = new SiteDataRuntime({
load: createFetchLoader(new URL('/site-data/', document.baseURI)),
});
const mounted = mountSiteDataRuntime({ runtime });The existing page supplies [data-site-banner], [data-site-commit],
[data-site-freshness], [data-site-content], [data-site-readiness],
[data-site-docs], [data-site-search], and optional
[data-site-claim="metrics.code.totalLines"] targets. unmount() stops polling and
removes the subscription.
index.html calls bootstrap.mjs to mount these targets and start polling.
The host supplies the publication directory and its /docs/<slug> router.
This repository entry point does not deploy or alter the owner-managed site.
The runtime verifies latest.json, the bundle, exact-CI evidence, and the
pointed files before installing a publication. A failed or rejected refresh
retains the last verified publication, while snapshot().state and
snapshot().banner identify syncing, blocked, stale, or unavailable.
The fallback is therefore never presented as current. stopPolling() releases
the timer when the page is disposed.
The consumer also checks document/search membership, safe slugs, and exact-commit source URLs before installing a snapshot. Markdown page links and anchors are validated by the publisher. Generated-publication tests exercise the full producer output; in-memory unit fixtures separately test lifecycle and rejection.
site-data-publish.yml now writes status.json beside latest.json for every
completed Build SparkEngine run on the current Working commit. A non-success
run writes blocked status while leaving latest.json and the last good
snapshot byte-for-byte intact. The reference runtime shows the status commit
separately from the content commit; retained content never acquires the failed
commit's SHA. A successful run still needs the existing exact-gate proof and
bundle validation before its content replaces latest.json.
The publisher checks the newest run and attempt for that SHA before preparation
and again before the ref moves, so a later dispatch cannot republish an older
successful attempt over a failed status.
The static runtime uses no credential. Before displaying current or blocked,
it queries GitHub's public API for the Working head and the complete bounded
Build run list filtered by source SHA. It compares repository, workflow, branch,
run ID, latest run number, attempt, status, conclusion, and head SHA with
status.json. A current bundle's Build evidence is checked the same way. A
missing API response, rate limit, moved head, newer run, rerun, truncated list,
or disagreement makes the view stale or unavailable. Hashes still protect the
retained bundle's bytes; the browser does not independently attest every
subordinate CI-120 or CodeQL field in its exact-evidence manifest. Those fields
remain subject to the publisher's exact-gate checks and must not be presented
as independently verified by the browser.
SITE_DATA_PUBLIC_REF selects refs/tags/site-data (the default) or
refs/heads/site-data. The owner must choose the ref actually served by the
public site's /site-data/ path. That publication-channel and live-site
adoption decision remains open; repository tests do not prove deployment.
The verifier and consumer tests use the same generated publication selected by
SPARK_SITE_DATA_DIR. Run them with:
$env:SPARK_SITE_DATA_DIR = '.site-data'; npm test --prefix tools/site-data/runtimeThe repository owner must create the stable-release GitHub environment before
dispatching a versioned build. This repository has one authorized human release
provider, Krilliac, so the environment uses an explicit owner-only contract:
configure exactly one required reviewer, the repository owner, and allow that
owner to approve a release they initiated. The required environment approval is
still mandatory; this is not an approval bypass and no second reviewer is
invented. The read-only verifier proves the reviewer identity through the
repository API. Use a custom deployment policy with exactly one branch entry
named Working. Do not allow wildcard or tag entries.
The controller runs from the current trusted Working commit, even when its
publication target is a version tag. Keep the existing Working ruleset enforced.
Disable administrative protection bypass in the environment's settings. The API
must prove can_admins_bypass=false; true, missing, or unrecognized values fail.
The workflow checks these API-visible protections before building, on entry to
the publication job, and immediately before publishing. The publisher job itself
is bound to stable-release, so GitHub applies the required owner approval gate.
Administrative bypass remains disabled. Nightly uses
the separate nightly-release environment and cannot qualify stable-v1.
verify_release_environment.py only reads GitHub metadata. Its token needs
repository Actions read access for the environment and deployment-branch APIs.
The owner must resolve a missing environment, API denial, or unavailable
protection feature; none is treated as approval. The preflight never creates an
environment, broadens token permissions, or supplies a substitute approval.
Immediately after that entry check, the publisher records the actual approval
event with record_release_approval.py. It reads
GET /repos/{repo}/actions/runs/{run_id}/approvals (bounded, Link-paginated,
duplicate-key-rejecting) and binds it to the run attempt, head SHA, release
workflow path, repository, and stable-release environment id. It fails unless
the history holds at least one approval, every review is approved by the
repository owner Krilliac (login and id) for stable-release only, and no
review is rejected. Each environment-bound job needs its own review, so a stable
run normally carries more than one owner approval; all of them are recorded.
The closed, deterministic record keeps the run id, attempt and start time, the
environment id, the approver, and a SHA-256 of each review comment. It is
retained as the 90-day artifact
stable-release-approval-<SHA>-<run>-<attempt> and its digest becomes the
job output approval_record_sha256. The approvals API exposes no review
timestamp, so approval time is bounded by the run start and that artifact's
upload. An owner-only approval is one person's review, not independent
second-person review; how GitHub reports that self-approval still needs a
hosted stable run to confirm.
The separate repository /immutable-releases API requires Administration
read permission, which GITHUB_TOKEN cannot provide. The owner must provision
RELEASE_POLICY_READ_TOKEN in both the stable-release and nightly-release
environments, using a GitHub App installation token or fine-grained token scoped
to this repository with Administration read (plus implicit Metadata read).
It needs no contents write or other write permission. Keep token issuance and
renewal outside this workflow's code changes; do not place it in source or a
repository variable. Policy proof runs after the environment-bound job starts,
not in the unprivileged preparation job. Missing, expired, or denied policy
authority fails closed and requires owner setup. Bindings are restricted to the
individual policy, guarded-mutation, staging, acceptance, and recovery steps;
there is no workflow/job-level binding. Checkout, download, upload, other
third-party actions, and unrelated repository steps do not receive this secret.
The policy reader supplies that secret only to its read subprocess. Mutation
commands keep the separate GH_TOKEN and do not receive the policy secret. The
policy credential is never passed on a command line, printed, or retained in the
evidence receipt; guard errors do not render mutation argv or Git auth headers.
The independent publication consumer receives neither environment secret.
Provision native Authenticode signing and the publisher thumbprint separately.
After the exact stable assets are frozen, the protected stable-release job
imports the same PFX with an ephemeral key and creates deterministic detached
signatures without exporting the private key. The derived SPKI SHA-256
fingerprint must match the protected SPARKENGINE_STABLE_SIGNATURE_KEY_FINGERPRINT
repository variable. The job uploads the resulting flat
SparkEngine-release-signature-bundle.tar.gz as an immutable release control
asset only after the durable download-counter preflight; the control asset is
not a distributable or badge-ledger entry. All existing signature, checksum,
SBOM, scan, exact-CI, source/tag, and package qualification gates still apply.
The prepare job's Compute release metadata step derives every channel tag
from two tested helpers; it holds no inline tag logic.
-
.github/scripts/stable_release_tag.py default-versionprints the singleset(SPARK_ENGINE_VERSION "X.Y.Z" CACHE ...)default and fails whenCMakeLists.txtdeclaresSPARK_ENGINE_VERSIONzero or several times. -
.github/scripts/stable_release_tag.py verify vX.Y.Zaccepts a dispatched stable tag only when it equals that default andCHANGELOG.mdhas exactly one## [X.Y.Z](optionally## [X.Y.Z] - YYYY-MM-DD) heading. -
.github/scripts/nightly_release_tag.py RUN_ID ATTEMPT SHAreturns the immutablenightly-<run>-<attempt>-<sha12>tag and rejects a missing or malformed run identity. Nightly has no versioned-changelog requirement.
Every rejection exits non-zero before any metadata output is written. The
contract suites run as the CTest selectors TagReleaseContract_StableTag and
TagReleaseContract_NightlyTag and in the validate-ci-tools job.
test-workflow-failure-propagation.py executes the workflow step itself against
fixture trees.
The stable release body is generated, not hand-written. After the signature
bundle is created and before the draft is staged, the Render fail-closed stable release notes step runs tools/release_notes.py and passes its output to the
stable staging step as RELEASE_BODY. The notes contain:
- the
stable-v1support matrix, supported hosts, experimental and unsupported capabilities, excluded gates, and limitations, copied fromdocs/site/readiness.jsonat the release commit (the profile state is printed as recorded, never upgraded); - the single
## [X.Y.Z]section ofCHANGELOG.md, with a### Migration(orMigrations,Migration notes,Upgrading,Upgrade notes) subsection surfaced as the Migrations section; without one the notes say that no migration subsection is recorded; - the asset list, the
SHA256SUMSlines, and fixedsha256sum -c,openssl pkey/openssl dgst -verify(with the pinned signer fingerprint),gh attestation verify, andgh release verifyinstructions.
The generator exits non-zero, and staging never starts, when the version section
is missing, duplicated, or empty; SHA256SUMS is empty, malformed, lists an
unexpected asset, or misses a distributable; the SBOM or SHA256SUMS is absent
from the expected asset list; or the signature control asset is missing, is not a
flat gzip tar with a manifest, public key, and signatures, or carries a key whose
SPKI SHA-256 differs from SPARKENGINE_STABLE_SIGNATURE_KEY_FINGERPRINT. The
output is deterministic and bounded to 120000 characters, so a resumed draft is
patched with identical text. Nightly releases keep their short workflow body.
ReleaseProfileRehearsal_ReleaseNotes (Tests/Tools/test_release_notes.py)
covers the renderer and its wiring; it is not the full REL-190 rehearsal.
Owner decision OD-17 (owner decisions, 2026-09-24) sets the retention and support terms each channel must meet. REL-100 owns enforcing them. They are policy targets; they do not mean any release has been published under them.
| Channel | Retention | Support |
|---|---|---|
| Stable | Release assets are immutable and kept permanently | Security and critical fixes until 6 months after the next stable release |
| Nightly | Each uniquely tagged immutable nightly is kept for 30 days | Unsupported |
| Experimental | Artifacts are kept for 14 days | Unsupported; always labeled experimental |
| CI build artifacts | Existing 7- and 90-day Actions retention | Not a release channel |
The stable window lines up with OD-03 (saves and scenes read N-1): a user on the previous stable release can still load current data during its fix window. No stable-v1 release exists yet, so no stable support window is running.
Where each term is stated or enforced today:
- Stable and nightly terms are stated in the generated stable notes
(
SUPPORT_WINDOWintools/release_notes.py), and the nightly release body states that nightlies are unsupported. - The 14-day experimental retention is pinned as
retention-days: 14on the experimentalbuild.ymluploads (ReleaseProfileRehearsal_ReleaseNotes). - Nothing enforces the 30-day nightly window yet, and no release text claims
it. Deleting an expired nightly release also deletes its assets, and
download_counter_ledger.pyrefuses any tracked asset that disappears without a durable pending publication. That would fail every later publication. A pruning job therefore has to retire those assets in the counter ledger first, keeping the cumulative download totals. Until it does, REL-100's channel criterion stays unmet.
Stable publication requires repository immutable releases enabled. All package,
signature, checksum, SBOM, exact-CI and source/tag checks complete against the
fully populated draft before the irreversible publication boundary. After
publication the returned record must be immutable, and gh release verify
verifies its automatic immutable-release attestation. A post-publication failure
preserves an immutable or ambiguous target for owner investigation. If an exact
fresh response instead proves that the same stable release ID, tag, and channel
are public and mutable, the acceptance/recovery helper may quarantine that target
as a draft and verify the result. It never infers permission to hide an immutable,
misidentified, or ambiguous release. Either failure leaves final readiness blocked.
Nightly publication is immutable too. release.yml sets RELEASE_IMMUTABLE
for both channels. Each nightly run creates a unique tag,
nightly-<run>-<attempt>-<sha12> from nightly_release_tag.py, and never moves
it. The legacy rolling nightly release is preserved and never mutated: its
hide/redraft step is a disabled migration tombstone, and
recover_release_publication.py returns without mutating a uniquely tagged
nightly. The policy preflight runs inside the
environment-bound publisher, and each ordinary publication/staging Git push or
API write checks again in its own call path. The narrowly proven mutable-stable
quarantine above is a containment exception, not permission to publish under an
incompatible policy. Draft staging checks before each create, update, asset deletion,
and upload, instead of delegating multiple unchecked mutations to a release
action. Policy-flip tests prove that later writes are refused after a compatible
policy changes. The legacy recovery writes that remain for the rolling
nightly release still require a mutable policy and refuse otherwise. The
workflow never toggles the repository policy automatically.
The migration to OD-17's target, uniquely tagged immutable nightlies, is in the workflow. No hosted run has yet published both channels under one immutable policy, so neither channel is described as operational. The REL-100 and REL-190 blockers stand until that evidence and the 30-day nightly pruning job exist.
The required Windows Shipping job creates and checks a real portable ZIP and NSIS package, checks executable and first-party module DLL release versions, and records/verifies stable-v1 toolchain provenance from the configured tree. The archive reconciler extracts final ZIP/TGZ contents under bounded path rules and binds the inventory to the artifact digest and committed dependency lock.
Release publication retains the lock-derived SPDX SBOM, reconciliation reports, and per-build toolchain records as release assets. They participate in the expected asset inventory, checksums, signatures for stable releases, and build attestations. The independent stable consumer requires them. Consumer release attestation verification also runs for nightly releases.
Native runtime MSI/NSIS packages and standalone bootstrap installers do not yet have verified final payload inventories. SDK header/library reconciliation cannot be applied to their different layouts by inventing not-configured declarations. The final-package gate records this gap and refuses publication; an outer installer digest or pre-CPack stage does not satisfy it. Completing native extraction and the runtime/standalone inventory contract remains required, unless the owner explicitly changes the criterion's scope. No scope exemption is introduced by these tools.
The ready-site gate checks publication evidence against GitHub's completed release run and its attempt-specific jobs, the immutable stable tag, and the released commit's ancestry to the deployed source. Evidence-recording descendant commits are allowed. Offline validation separately requires the finalization gate and every profile's finalizer evidence to name the same released commit. These checks are tested locally; they do not constitute hosted publication evidence.
After publication, verify-stable-publication runs on a fresh runner with only
Actions, contents, and attestations read permissions. It obtains the exact
published asset IDs through GitHub, downloads the stable assets and the
signature control asset, checks their sizes and SHA-256 digests, extracts and
verifies the pinned detached signatures and SBOM, compares provenance to freshly
revalidated exact-CI evidence, verifies the GitHub release attestation, and
rechecks the release, assets, and tag for drift. Before downloading anything it
rebuilds the approval record from the GitHub API for the publisher's attempt and
requires the publisher's exact approval_record_sha256; the receipt embeds it.
It cannot publish, edit the ledger, or turn the profile ready.
Only success produces the immutable Actions artifact
stable-publication-evidence-<SHA>-<run>-<attempt>, retained for 90 days. The
receipt says publication-verified, carries the candidate SHA, release and asset
IDs/digests, and verifier run identity. Preserve that artifact and its GitHub
artifact digest/producer identity in the release evidence archive before expiry;
an unverified local JSON copy is not authority. A failed verifier run produces no
success artifact, even if a late local publication error leaves a visible JSON
file. Final readiness remains blocked pending investigation and recovery.
The receipt writer accepts at most 1 MiB and requires an existing real parent
directory. It rejects existing names and symlink/reparse traversal. Linux
publication anchors an anonymous O_TMPFILE inode to a directory descriptor,
syncs the completed file, atomically links the owned fd without replacement, and
syncs the directory before verifying the visible file and parent identities.
The Linux-documented proc fd link mechanism
needs no elevated capability. Missing O_TMPFILE, proc fd access, or directory
sync support fails closed; other POSIX platforms have no named-temp fallback.
Pre-link failure closes the anonymous inode, leaving no staging name to clean
up. Post-link failure never deletes a visible name: it reports that publication
or durability is unconfirmed, preserves any competing replacement, and requires
investigation rather than issuing a successful receipt result. Windows retains
the unchanged native package writer under locked parent directories. Neither
backend overwrites an existing receipt.
The release owner must then reconcile this independent evidence, actual
deployment approval, all supported-host/rehearsal results, and live-site
consumption at the same released SHA. Only that completed review may close the
declared finalization work and promote all required gates/profile/global state.
--require-ready remains the final fail-closed validator. A successful publisher
or consumer alone is insufficient. The released candidate commit is immutable;
later evidence-recording commits must retain that exact artifact source identity.
The maintainer chose a signed, immutable v0.9.0 stable predecessor before
v1.0.0, rather than a first-release bootstrap exception. The strict MSI
qualifier must continue to require a real previously published, signed,
non-prerelease stable MSI and its matching manifest. Publish v0.9.0 only after
its own applicable signing, package, publication, and acceptance evidence is
complete; then qualify the v1.0.0 upgrade, rollback, repair, and user-data
retention gates against those exact immutable predecessor assets. The choice
does not mark either release ready or waive any gate. Until v0.9.0 is
independently verified as published, the provisioner correctly rejects
v1.0.0. Fabricating an older release, treating nightly as the predecessor,
or marking upgrade/rollback done without the real transaction is prohibited.
The maintainer chose to derive a reviewed v0.9.0 candidate from the current
release branch in an isolated checkout after its source batch is sealed. That
does not authorize relabeling the current 1.0.0 checkout in place or
manufacturing an old tag: the candidate needs its own reviewed version and
changelog commit, exact-source CI, signed artifacts, and publication evidence.
The release controller requires the stable tag to match the single CMake
version default and its changelog heading. No v0.9.0 source/tag or published
release currently satisfies that rule. The existing
versioned MSI job also always requires a prior release; it is intentionally not
a v0.9.0 bootstrap path. Before any v0.9.0 dispatch, the owner must review
and identify an authentic 0.9.0 source commit with corresponding version and
changelog, and approve a separate one-time native qualification contract for a
signed clean install, repair, runtime smoke, uninstall, and user-data retention
without pretending to perform an upgrade or rollback. That contract must still
pass the applicable exact-source CI, package, signing, protected-environment,
immutable-publication, and independent-consumer gates. Only after the resulting
v0.9.0 release is independently verified should the normal v1.0.0 gate run
its actual old-to-new upgrade/rollback transaction. The normal provisioner pins
v1.0.0 specifically to an immutable v0.9.0 release; a different earlier
version or a mutable release cannot substitute for it.
Owner decisions OD-18 and OD-19 (2026-09-24) are recorded in
predecessorRelease in docs/site/readiness.json:
- In the v0.9.0 predecessor stage, REL-191 replaces REL-190 through
qualificationSubstitutions, next to the existing REL-192→REL-191 and INST-131→INST-132 entries. REL-190 stays required for the stable-v1 release candidate and remains in both blocking lists. REL-191 already covers every common qualification gate and named sign-off without N-1 claims. -
ownerisKrilliac. - The baseline is not picked by hand. It is the first
Workingcommit after the stable-v1 release branch merges at which everypredecessorRelease.requiredGateIdsgate has passing exact-SHA evidence. Until thensourceCommitEvidence.baselineCommitandsignOffEvidencestay empty and the stage staysblocked;--require-predecessor-candidaterejects the empty baseline.test_release_stages.pychecks that these fields stay empty while any required gate is not passing.
tools/site-data/workflow_ownership.py binds experimental Shipping preset uses
in workflow commands, including matrices and declared environment variables, to
platform work or named runtime test hosts. Platform certification stays advisory
and outside the Required CI Gate. The experimentalInstallerProducts registry
in docs/site/readiness.json binds the actual nightly installer matrix artifacts
to their experimental platform capabilities and work items; it does not add
those installers to the stable profile.
build.yml/site-data-full-determinism compares two clean site-data generations,
each regenerating documentation health independently. Dirty checkouts fail the
proof. The existing advisory determinism job still checks its narrower bundle;
only the required full proof covers independently generated health.
Release-profile SparkTests selectors inspect each test body and reachable local helpers for declarations from included production headers. Utility-only includes and constant assertions cannot establish production evidence. This source check is lexical: it does not prove C++ name resolution or assertion dependence, and raw whole-suite count floors remain a separate limitation. Module evidence also rejects copied or tautological bodies when deriving its expected test set.
Implemented 2026-09-21. Sources: readiness contract,
release workflow, and
GitHub environment API,
and the repository immutability API.
Predecessor identity and source-lineage review updated 2026-09-22; see the
release API immutable field.
Approval-event recording added 2026-09-24 against the
workflow-run review history API.
Recheck live environment protection, approval history, signing authority, and
exact-SHA run/artifact identities before each release.
Channel retention/support policy (OD-17) and the predecessor owner, baseline
rule, and REL-190 substitution (OD-18/OD-19) recorded 2026-09-24 from
owner decisions.
Site-data status trust model updated 2026-10-01 from
the publisher,
the reference verifier,
GitHub's workflow-run API,
CORS guidance,
and unauthenticated rate limits.
Contract reference rules and the public numeric-claim ledger added 2026-09-24 from
validate.py and
test_site_data_contract.py.
Generated stable release notes (REL-190) added 2026-09-24 from
tools/release_notes.py.
Nightly retention status and the immutable-nightly migration status (REL-100)
updated 2026-09-28 from release.yml
and download_counter_ledger.py.
Release tag contract helpers (REL-100) added 2026-09-26 from
stable_release_tag.py and
nightly_release_tag.py.
Published from 614bca17b41e. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- CI Controlled-Failure Rehearsal
- Release Publication Stages
- Session Handoff 2026-09-27
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Online Service Boundary
- Server Operations Runbook
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers