Project layout
The files and folders of a Qsolve workflow repository, what each one is for, which are required, what is left out of the published snapshot and the size limits.
Updated 13 November 2026 View as markdown
On this page
Qsolve reads qsolve.yaml at the root of the workflow, and the two schemas, the example inputs and the entrypoint module at the paths qsolve.yaml gives. benchmarks/cases.yaml is read at that fixed path. Everything else is yours.
The workflow does not have to be the whole repository: when you submit, you can name a subfolder (for example workflows/max-cut), and that folder is then the root described here. It must be a relative path of letters, digits, ., _, - and /, never absolute, never .., never starting with a dot. If it holds no qsolve.yaml at the chosen commit, the submission is refused with No qsolve.yaml in the folder '<folder>' of this repository at that commit.
The layout
This is the published max-cut example, which every example follows:
max-cut/
qsolve.yaml required
README.md recommended
LICENSE recommended
schemas/
input.json required
output.json required
src/
__init__.py keep it: the examples all have one
workflow.py your Workflow class
qubo/ your own modules (QAOA, classical solvers)
examples/
ring_6.json required: at least one example input
benchmarks/
cases.yaml required to publish
inputs/ larger benchmark inputs
tests/ your own tests, not published
requirements.lock optional
| Path | Required | Purpose |
|---|---|---|
qsolve.yaml |
Yes | The manifest: identity, schemas, parameters, methods, limits. See The manifest. |
schemas/input.json |
Yes (path is set by inputs.schema) |
JSON Schema (draft-07) for the input. Every input is validated against it and the input form is built from it. See Input and output schemas. |
schemas/output.json |
Yes (path is set by outputs.schema) |
JSON Schema for the answer answer() returns. The answer is validated and rendered from the schema's titles and units. |
src/ |
Yes | Your code. The manifest's entrypoint points into it. The lint looks for the entrypoint's module both at the repository root and under src/ (src.workflow is src/workflow.py or src/workflow/__init__.py); a missing module or class is E030. |
examples/*.json |
At least one (E024) |
Example inputs listed under inputs.examples; the file names and folder are your choice. Each is validated against the input schema and shown to researchers as a starting point. |
benchmarks/cases.yaml |
To publish (E050) |
Known-answer cases the platform runs and records. See Benchmarks. |
README.md |
Recommended | What the workflow solves and its limits. Lint warns (W060) when missing. |
LICENSE |
Recommended | Lint warns (W051) when no LICENSE, LICENSE.md, LICENSE.txt or COPYING exists. The manifest's license field is required either way. |
tests/ |
No | Your own pytest tests. Never shipped or run on the platform, and exempt from the import check. |
requirements.lock |
No | Documents your packages. It installs nothing; its only effect is that lint reports an import of a package listed there as W042 instead of E040. |
Entrypoint and package name
The manifest's entrypoint is package.module:ClassName. The examples use a package called src, so the entrypoint is src.workflow:MaxCut. Keep src/__init__.py and import your own modules relatively:
from .qubo import Qubo, plan_qaoa, sample_qaoa
from qsolve import Context, Estimate, InvalidSolution, Outcome, Workflow, WorkflowError
Importable modules
Workflows run with no network and no package installs, and your own Dockerfile is ignored. Your code may import:
- the Python standard library;
qsolve;numpy,scipy,sklearn,qiskit,qiskit_aerandpyscf;pydantic,jsonschema,yaml,typerandrich.
Anything else is lint error E040. A package pinned in requirements.lock but unavailable produces warning W042, and the run rejects the import, so a workflow that needs another package cannot be published today. Files under tests/ are exempt.
Data files
Ship the data your workflow needs inside the repository and read it from disk, as electrolyte-solvent-screen does with src/geometries.json. A run sees only the repository and a scratch directory. Data-preparation scripts that need other packages belong outside the repository or in a folder your code never imports; the examples keep them in a separate tools/ folder.
Left out of the published snapshot
Publishing stores an immutable copy of the repository. These are skipped:
- directories named
__pycache__,.git,.venv,.pytest_cache,.ruff_cache,.mypy_cache,node_modulesandtests, wherever they are in the tree, and any file or directory whose name starts with~; .pycand.pyofiles,.DS_Store,Thumbs.dbanddesktop.ini.
Everything else, including the README, the LICENSE and your example inputs, is in the snapshot, so changing any of it needs a new version, and changing only what is skipped (for example a test) does not. See Versioning.
Limits and rejected content
| Rule | Limit | Lint code |
|---|---|---|
| Files in the repository | 500 | E011 (the snapshot step refuses the same limit with workflow too large) |
| Total size of the files | 20 MB | E011 (same) |
| Each schema file | 256 KB | E022 |
| Repository size as fetched | 50 MB | rejected at fetch |
| Symbolic links | None | E010 |
| Git submodules | None | rejected at fetch |
Files that look like credentials (.env, *.pem, *.key, id_rsa*, credentials*.json, .netrc) |
None | E012 |
Text that looks like a committed secret (private keys, cloud and GitHub tokens, sk_live_ style keys) |
None | E013 |
The lint counts everything except __pycache__, .git, .venv, .pytest_cache, .ruff_cache, .mypy_cache and node_modules, so it counts tests/, while the snapshot step does not. The secret scan skips files over 1 MB and common binary types (.png, .jpg, .gif, .pdf, .zip, .npy, .npz).
Never put a provider token in the repository; your code never needs one. Every lint code is in Lint codes.