Contributing#
SpatialData is shaped by the people who use it. Whether you write a reader in another language, build a viewer, run into a dataset the format cannot represent yet, or just find a sentence here unclear: your experience is what makes this specification better, and every contribution counts, from a typo fix to a new element type. You do not need to be a SpatialData developer to take part; questions and half-formed ideas are welcome.
The quickest way in is to say hello on the #spatialdata-dev channel of the scverse Zulip, where the developers discuss the specification and its implementations.
How to take part#
Discuss an idea on Zulip, or suggest a change in a GitHub issue, ideally together with a pull request.
A particularly large feature or a big breaking change comes with an RFC (see RFCs).
Once an issue or RFC is approved, the change lands in the next version of the specification (see Upcoming version (0.5)); the Compatibility matrix records which implementation releases support it.
A change to the in-memory format of one language does not always require changes in the others, but sometimes it affects several languages. If in doubt, open an issue in the corresponding implementation repository, or tag its developers, to make them aware:
Python: scverse/spatialdata
JavaScript: Taylor-CCB-Group/SpatialData.js
RFCs#
An RFC (request for comments) is a written proposal that is discussed and refined in the open before anything is decided. Smaller changes go through an issue and a pull request alone. All RFCs are listed on the RFC page.
When to write an RFC#
The change adds a concept to the abstract data model, or changes the meaning of an existing one.
The change needs a new storage format version and affects several element types, or affects readers and writers in more than one language.
There are several reasonable designs, and the choice needs input from the community.
Steps#
An RFC and the change it proposes are two pull requests:
Propose. Open a pull request against the branch
sitethat adds the RFC assite/rfcs/<number>-<name>.md, from the template below, with the status draft. Numbers are sequential in order of proposal and never reused (e.g.site/rfcs/0005-tiling.md). An RFC that started elsewhere (a discussion, a fork) gets a short file with a link, and its text moves into the file when it is presented for review.Review. Once the text can be reviewed, the pull request is merged with the status under review, so that the RFC is on the RFC page while it is discussed. Discussion goes to an issue linked from the RFC; revisions are further pull requests to the same file.
Change the specification. When the design is concrete, open a second pull request, against
main, with the changes to the specification pages, and put its link in the RFC’sspec-pr.Decide. The SpatialData developers decide, following the consensus-seeking process of the scverse governance. The decision and its reasons are recorded in the RFC, with the status accepted or rejected; a rejected RFC stays on the RFC page as a record. On acceptance, the specification pull request is merged into
main, the draft of the next storage format version (see Upcoming version (0.5)) and, if it changes the data model, of the next data model version; otherwise it is closed.Release. The status becomes implemented when that version is released, which requires an implementation that reads and writes it.
Template#
---
rfc: <number>
title: "<title>"
scope: "data model; storage format; implementations"
targets: "data model 1.1 (or: data model unchanged); storage format 0.5"
proposed-by: "<names>"
status: "draft"
spec-pr: <link to the pull request against main, once it exists>
---
# RFC <number>: <title>
Based on: <link to the tag of the specification it changes, e.g. storage-format-0.4>
## Authors and acknowledgements
## Summary one paragraph: what changes, for whom
## Motivation the problem, with concrete use cases
## Proposal
### Data model new or changed concepts, in plain language
### Storage format the bytes that change; new storage format and container version
### In-memory formats per language, where affected
## Compatibility reading old stores, writing new ones, migration
## Alternatives designs considered and why they were not chosen
## Open questions
## Decision filled in when the RFC is accepted or rejected
## References
Versions and releases#
Each commit of the branch main holds one version of the specification: one storage format
version and the data model version it encodes. Changes to the specification are pull requests
against main. A change that needs a new version makes main the draft of that version: its pull
request also gives the draft’s container format version a -dev suffix, marks the version as
draft in the version history, and lists the change in Upcoming version (0.5). Further changes for the
same version edit the draft; corrections that change no version are made the same way. Releases
are tags storage-format-<version> of main and are never moved.
When a new version starts#
The specification decides when a version starts and when it is released, following the decision process, independently of any implementation. A new version is opened when at least one of these is true:
a change to the stored bytes is needed (new key, new payload, changed meaning of an existing key);
a new element type is introduced;
an upstream standard the format depends on (NGFF, AnnData, GeoParquet) releases a version worth adopting, such as NGFF 0.6 with RFC-5 transformations.
Bug fixes to the prose, clarifications and non-normative additions do not open a new version.
Release#
A version is released once its accepted changes are complete and implemented in at least one implementation, with regenerated example stores. Then, in one pull request:
its container format version loses the
-devsuffix;Storage format version history (and Data model version history, if the data model changes) list it as current, and the previous version as deprecated;
the commit is tagged
storage-format-<version>and the tag is added toversions.tomlon the branchsite, which publishes it on the website.
Released versions are not changed afterwards: corrections go into main and are published with
the next release. Implementations adopt the version in their own releases; the
Compatibility matrix records which releases read and write it.
Structure of the specification pages#
data-model.md holds the abstract data model. Each section’s text, below its heading, sits
between two comment markers named after the section’s label:
(spec-tables)=
## Tables
% begin spec-tables
...
% end spec-tables
storage-format.md has the same sections. Each one repeats the heading, includes the data
model text between the markers, and follows it with the storage format:
(storage-tables)=
## Tables
:::{include} data-model.md
:start-after: % begin spec-tables
:end-before: % end spec-tables
:::
(storage-tables-layout)=
### Layout
...
A change to the data model is therefore made once, in data-model.md, and appears on both pages.
On the storage format page, the included text is marked Data model with a line beside it.
Data model sections are normative, and readability comes first: plain, human-readable English for everyone, with RFC 2119 key words only where they make a constraint clearer. Prefer positive statements. Keep implementation details out; move history and outlook into footnotes (
[^name]), with the footnote text inside the section’s markers so that it is included with it. On screen, each footnote shows at its reference on hover or focus; in print, the page’s last section, Notes, lists them.Storage format sections are normative and must be unambiguous. They state only what the storage format adds, and link to the data model for the concepts (
{ref}to thespec-*anchors) instead of repeating them. Give every subsection heading astorage-*label.Section headings cannot go inside tabs (such as the transformation types). There, use a rubric with a label, which gets a “¶” permalink that also records the selected tabs:
(storage-tables-layout)= ```{rubric} Layout ```Figures go in
img/and are included with{figure},:figclass: spec-figureand an:alt:text.
Writing rules#
Where key words are used (always in the storage format), use MUST, SHOULD, MAY per RFC 2119, in plain capitals (not bold), and mean it.
Specify only what NGFF, AnnData, Zarr, Parquet or GeoParquet leave open; link to them for the rest.
Write the pages as the specification of their version alone, without referring to other versions; the version history is the only place that compares versions.
The normative text specifies the format, not an implementation: name the
spatialdatapackage (or another implementation) only in implementation notes and footnotes.Use the custom admonitions:
Implementation note (
implementation-note, non-normative);Divergence from NGFF (
ngff-divergence).
The pages state what is decided. Open questions go into an issue or an RFC, and candidate changes into Upcoming version (0.5).
JSON examples come from real stores; abbreviate with
...if needed.Cite as a link, for example
([Marconato et al., 2025](https://doi.org/10.1038/s41592-024-02212-x)).Short sentences, no filler. If a paragraph does not change what an implementer would do, delete it.
Building the site#
The website, including this guide and the RFCs, is on the branch
site, which builds every tagged version
into one site; its README describes how to build and preview it locally.
Licensing#
The text of the specification, of this guide and of the RFCs is licensed under CC-BY-4.0; the code (the website and the tools) under BSD-3-Clause. A contribution is made under the same licences.