sphinx-needs
This repository is a uv workspace:
one package per distribution under packages/, and a root that is never built or published.
The root depends on every package, owns the dependency groups they share, and holds
repository-level policy — the lock file, the lint and type-check configuration, the task
definitions and the CI workflows.
| package | distribution | what it is |
|---|---|---|
| packages/sphinx-needs | sphinx-needs | the Sphinx extension for managing requirements and specifications — documentation, README |
| packages/sphinx-mounts | sphinx-mounts | the Sphinx extension that mounts external source trees into a build without copying or symlinking — documentation, README |
| packages/sphinx-codelinks | sphinx-codelinks | fast source-code traceability for sphinx-needs — it scans source files for comment markers, turns them into needs, and links documentation to exact source lines — documentation, README |
| packages/sphinx-test-reports | sphinx-test-reports | test results as needs: JUnit/ctest/googletest XML and tox-envreport JSON become needs in a build, and a test-reports command turns the same reports into a needs.json without running Sphinx — documentation, README |
Why one repository, and why still several packages
The question comes up, so here is the reasoning. The proposal and its discussion are in #1803.
One repository, because the extensions are developed against sphinx-needs as it is now: a change to sphinx-needs and the extension change it calls for land together, tested against each other at the same commit, with one lock file, one CI, one lint and typing configuration and one issue tracker.
Still one distribution per package, and not one sphinx-needs with an extra per feature,
because:
- An install is whole or absent. An extension's dependencies — parser grammars, a
- A Python extra is not a feature flag. It is a set of additional requirements, and
setup(), its console scripts and its type checking, and a user who
later uninstalls the dependency finds out at build time. sphinx-needs pays that once, for
matplotlib, and does not want to pay it per extension. A distribution boundary is
resolved by the installer, visible to the type checker and versioned.
- Each package keeps its own version and cadence. An extension can change its
- Not every package depends on sphinx-needs. sphinx-mounts does not, and an
The coupling that remains is a policy, and tooling keeps it honest: a package that depends
on sphinx-needs requires the current release as its floor and caps at the next major (the
workspace check enforces the range), the release workflow tests every built wheel against
its siblings as published, and uv run poe release-plan says what is pending and in
which order.
Working here
Two commands are enough to get started, from this directory:
uv sync --frozen # every package, plus the shared development and test dependencies
uv run poe # list every task, with its help
No group has to be named: the test tooling is a group of the workspace root, and the root's
default dev group includes it.
Tasks that act on the whole repository are named plainly (lint); tasks that act on one
package end in that package's short name (test-needs, docs-needs, docs-mounts).
Contributions are very welcome — see
the contributing guide, and AGENTS.md for
the repository's layout in more detail (CLAUDE.md only imports it).