The module and stack registry for AirStack —
the marketplace index, built on the
rosdistro pattern: an index repo of one small YAML per entry, version-pinned
refs, and CI-verified compatibility. airstack module search|list|add reads this
repo; the AirStack docs site renders it as the marketplace catalog with badges.
airstack-modules-index/
├── schema/ entry schemas (module-entry, stack-entry)
├── modules/ one YAML per registered module
├── stacks/ one YAML per registered stack (trunk reference stacks + external)
├── compat/ VERIFIED compatibility matrix — CI-stamped ONLY, never hand-edited
├── releases/ release sets: tagged reference-stack pins blessed by one CI run
├── rfcs/ interface-conventions deprecation RFCs
├── unmaintained/ the shelf: entries whose canary went red and stayed red
└── tools/ validate_entry.py — the schema gate CI runs on every PR
Getting listed = opening a PR to this repo that adds modules/<name>.yaml.
The PR review is the quality gate. Checklist a registration PR must clear:
- Schema-valid manifest — the module repo's
module.yamlpasses trunk'stools/validate_module.pyagainstcommon/module_schema/module.schema.json. - CI green — the module's CI calls trunk's reusable
module-system-tests.ymland passes its declared marks. See docs/development/module_ci.md for wiring the ~12-line caller and choosing marks. - README per template — the module README follows the template
(scaffolded by the
create-module skill
/
airstack module create); it is embedded on the versioned docs site. - License check —
license:is a real SPDX-style license, surfaced here in the entry (a GPL module in an overlay chain must be visible, not buried). - Registry entry valid —
modules/<name>.yamlpassespython3 tools/validate_entry.py(CI runs it on the PR; maintainer email and non-empty license are hard registration gates).
Registered stacks get catalog entries too (stacks/<name>.yaml): description,
target trunk range, and the docs site embeds the stack's CI-generated wiring.md
from its home repo.
An entry's airstack_compat is the DECLARED range, copied from the module's
module.yaml. The VERIFIED matrix lives in compat/, where rows
are stamped exclusively by CI runs of module-system-tests.yml (via the
compat-stamp repository dispatch — see
.github/workflows/compat-stamp.yml).
compat/ is CI-stamped only — never hand-edited. A PR that hand-writes a
compat row is rejected on sight; a compatibility claim that isn't CI-verified
rots — that is the registry's core design principle.
Badge semantics: a compat badge reads "module M @ vM passes marks {…} in a test stack derived from reference stack S, on AirStack vX" — conformance to a stack, demonstrated by flying it.
- Maintainer lifecycle. Every entry names a maintainer (email, required).
The nightly canary runs registered modules against trunk
develop; a canary red N weeks unanswered auto-moves the entry to theunmaintained/shelf — still listed, clearly labeled, never green-badged. Maintainers resurface, entries move back. - License checks at registration — the
licensefield is a hard gate invalidate.yml. - Conventions deprecation policy. The interface conventions spec is public
API even though nothing compiles against it. Changing a canonical topic name,
type, QoS profile, or frame convention requires semver-major on the spec, a
coexistence window, and a short RFC in
rfcs/— landed here before the change ships. airstack_msgsis semver'd ruthlessly as its own package — ROS 2 type hashes make message mismatches fail silently, so message-version discipline is what keeps badges meaningful across trunk versions.
A release set is tagged reference-stack pins, blessed by one CI run of the
whole stack: "AirStack 0.20 + full_default @ tag" — see
releases/. Individual badges mean "works in a reference stack";
a release-set tag means "this whole stack, these exact pins, passed together."
pip install pyyaml
python3 tools/validate_entry.py # all of modules/ + stacks/
python3 tools/validate_entry.py modules/optitrack.yamlJSON verdict on stdout, human-readable errors on stderr, exit 0/1 — the same
contract as trunk's validate_module.py.