Versioning
How Qsolve versions a workflow: the semver string in qsolve.yaml, the content digest and what it covers, what happens when you resubmit a version, what is stored per version, which version runs and what happens to old results.
Updated 13 November 2026 View as markdown
On this page
Each published version of a workflow is an immutable snapshot identified by the name and version in qsolve.yaml. To update a published workflow, change the code, raise version, push and submit the repository again. Results already produced stay attached to the version that produced them.
The version field
version is MAJOR.MINOR.PATCH: three whole numbers separated by dots, such as 1.0.1. The rule is ^\d+\.\d+\.\d+$; anything else fails with version must be semver, e.g. 0.1.0. A pre-release suffix such as -beta, a leading v and a two-part version are all rejected. Write all three parts, because YAML reads 1.0 as a number, which is rejected as well.
What the platform enforces
The platform enforces one rule, and it is about content, not about the numbers:
A
nameandversionthat already exist may only be submitted again with identical content. Different content under the same version is refused.
It does not compare version numbers with each other. Nothing requires the new version to be greater than the old one, that the bump be of any particular kind (major, minor or patch), or that numbers be consecutive. "Bump the version" therefore means: change the string so the name and version pair has not been published before.
A convention that helps researchers read your history:
| Change | Bump |
|---|---|
| A fix that does not change what the workflow computes | patch |
| A new optional input field, parameter or method; better performance | minor |
| A change to the input or output schema that breaks existing inputs or readers | major |
The content digest
Every submission is reduced to one digest of the workflow's files. That digest is what is compared when a version is resubmitted. It is computed like this:
- Take every file under the workflow folder, except the files left out of the snapshot: the cache and tool directories, any
testsdirectory, files and folders starting with~,.pycand.pyofiles,.DS_Store,Thumbs.dbanddesktop.ini. - Sort them by path, comparing each path part by part and case-sensitively, so the order is the same on Windows and Linux.
- For each file, in that order, feed SHA-1 the path relative to the workflow folder (with
/separators) and a zero byte, then the raw SHA-256 digest of the file's bytes. - The 40-hex-character result is the digest.
What follows from that:
- Everything that is stored counts, including
qsolve.yaml, the schemas, the example inputs,benchmarks/,README.mdandLICENSE. Editing a typo in the README changes the digest, so it needs a new version. - Bytes count, not meaning. Line endings, trailing whitespace and file renames all change the digest.
- What is skipped does not count. Changing only a test, or a cache file, leaves the digest unchanged. Empty folders do not count either.
- The digest is not a git commit. It is computed from the files, so a new commit with identical files gives the same digest. The real git commit of the submission is stored separately as the version's source commit.
- Symbolic links are an error (
symbolic links are not allowed: <path>), and so is a tree above 500 files or 20 MB (workflow too large (<n> files, <bytes> bytes)).
The digest also names the stored copy: the snapshot directory is the digest itself, so two versions with identical files share one copy.
Resubmitting a version
When the submission passes validation and reaches publishing, the platform looks for a published version with the same name and version:
| Situation | Result |
|---|---|
| No such version | A new version is stored and becomes the latest. |
| Same version, same digest | Accepted and nothing new is created. The existing version is untouched, apart from filling in two stored fields that an earlier publish could not set (its hardware cases, if they were empty, and its storage bundle). It does not become the latest version if a later one exists. |
| Same version, different digest | Refused right after the static checks, before the sandbox gates: Version 0.1.0 of depot-picker already exists with different content; bump the version in qsolve.yaml and resubmit. (with your workflow's name and version). The registry repeats the check at publish as a safeguard. |
The conflict check runs after fetch and lint and before the expensive sandbox gates, so a content change under an old version number is caught in seconds; see Validation and publishing. An identical resubmission still runs the gates, is adopted as it is, does not change the latest version (the report says "the latest version is unchanged", including when it is an older version) and does not record the benchmark history a second time.
Name rules at the same stage:
- A
namethat belongs to another developer is refused:The name <name> belongs to another developer. Choose a different name in qsolve.yaml. - A
nameyou deleted stays reserved:<name> was deleted and its name stays reserved. Choose a different name in qsolve.yaml. - A new
namecreates a new workflow. Its display title is made from the name (max-cutbecomes "Max Cut") when the workflow is first created, and later versions do not change it.
What is stored per version
| Stored | What it is |
|---|---|
| The version string | 1.0.2, unique within the workflow. |
| The content digest | The 40-hex digest above. Stored in the version's commit_sha field, which is the digest, not a git commit. |
| The source commit | The real git commit the submission came from, when it came from a repository. |
| The files | A read-only copy of the snapshot, named by the digest, which runs mount into the sandbox. When storage is configured a bundle of it is also kept, with its SHA-256. |
| The manifest | The full qsolve.yaml as validated, including the defaults you did not write. Runs of this version are checked against this copy, not against the repository. |
| The schemas | The input schema, with the order of each object's properties recorded as x-order so the form shows fields in your order, and the output schema. |
| The examples | The contents of every file listed in inputs.examples. |
| The hardware cases | The hardware_cases of benchmarks/cases.yaml with their inputs written out, at most 600 KB in total. |
| Status and time | published, with the publish time. Versions are published only after every gate passes. |
Each new version also refreshes the workflow's own summary, family and tags from its manifest, points the workflow's latest version at the new one, and adds an audit-log entry recording the version, digest and whether it is first-party. The workflow's visibility is not touched.
Publishing an update
- Change the code or files.
- Raise
versioninqsolve.yaml. - Run
qsolve validate . && qsolve lint . && qsolve test .. - Push to GitHub.
- Submit the same repository address again, with the new branch, tag or commit if you use one.
The new version goes through every stage in Validation and publishing independently, and its benchmark history starts afresh. The name identifies the workflow; changing it publishes a different workflow.
Publish versions in increasing order. The most recently published version becomes the latest whatever its number, so publishing 1.0.1 after 1.1.0 makes 1.0.1 the latest. A new version of an approved workflow keeps its visibility and does not wait for approval again; only the first version of a new workflow is subject to the approval setting.
Which version runs
A run in the web app uses the latest version. Older versions stay published, and the track record shown for a workflow is that of its latest version. A run created through the API may name an older version with version (MAJOR.MINOR.PATCH); without it, the latest is used. An unknown name or version answers 404 workflow_not_found.
What happens to old results
Every run records the workflow version, SDK version, input hash, parameters, seed and result hash, and belongs to the version it ran on:
- A new version never rewrites old results. A result produced by
1.0.0stays a1.0.0result, with the output shape that version defined. - A new version has its own benchmark history, so researchers comparing versions compare their records.
- Because versions are immutable, a run is reproducible: the same version, input, parameters and seed give the same result hash on a simulator.
If you change your output schema in a way that breaks readers of old results, raise the major version so the break is visible.