0017: First-party plugins live in the core repository as workspace packages¶
Context and Problem Statement¶
Integrations (Atlassian, Google Workspace, Slack, …) are plugins: optional, with their own dependencies. Many of them are maintained by the core team and should move in lockstep with the kernel; others belong to companies, sites or teams and live in their own repositories.
Considered Options¶
- First-party plugins as uv workspace members in
plugins/, released with the core (chosen) - Every plugin in its own repository: kept for external plugins, which are scaffolded from
template/.
No other alternatives were recorded when this decision was made.
Decision Outcome¶
Chosen option: first-party plugins live in plugins/ of this repository as workspace packages, because they
move in lockstep with the kernel while users only get the dependencies they need.
- First-party plugins live in
plugins/<distribution>/of this repository as uv workspace members: separate distributions (heinzel-atlassian,heinzel-google, …) with their ownpyproject.tomlandheinzel.modulesentry point, held to the same import contracts as external plugins (public API only). - They are versioned, tested and released together with the core: same tag, same changelog, same CI and reviewers.
- Installing one is short:
hz system plugin add atlassianresolves a first-party name to the package at the installed core version; extras (heinzel[atlassian]) work too. Users only get the dependencies they need. - External plugins live in their own repositories, scaffolded from
template/. A proven external plugin can be promoted intoplugins/unchanged, because it already has the same shape. plugins/holds the first-party plugins and nothing else, to keep "plugin" unambiguous (see ADR 0018 foragent-skills/). The maintainer named the directoryplugins/on 2026-10-24 ("it contains plugins"; ADR 0028 More Information); it had another name before.
Consequences¶
- Bad: the release tooling builds, versions and publishes several distributions from one tag.
- Neutral: implemented with the first integration, so the tooling is shaped by a real package.
Confirmation¶
- import-linter contract "First-party plugins use only the public API, like any plugin" in
pyproject.toml. tests/unit/repository_test.py::test_first_party_plugins_match_the_plugins_directorykeepsFIRST_PARTYinsrc/heinzel/modules/system/plugin/_service.pyequal toplugins/;tests/unit/modules/plugin_service_test.pycovers resolving first-party names.- CI and
publish.ymlbuild every distribution withuv build --all-packages.
More Information¶
The record was proposed with "implemented with the first integration (Atlassian), not before". It was implemented
with the clock plugin (plugins/heinzel-clock) instead and accepted on 2026-10-23.