ADR-002: Plugin contract versioning
- Status: ACCEPTED
- Date: 2026-05-08
- Deciders: cproudlock
- Supersedes: none
Context
Once sister sites start writing their own plugins (or pulling community plugins), the framework's plugin contract becomes a public API. Without a versioning story, any change to BasePlugin or the core platform models can silently break installed plugins at remote sites.
The existing BasePlugin and PluginMeta already declare a core_version field (default ">=1.0.0"), but it is not enforced anywhere. The plugin loader does not check it before instantiation.
Decision
The framework adopts semantic versioning for the plugin contract, declared in two places:
- Framework version (
shopdb/__init__.py): a single__contract_version__constant. This is the version of the platform contract as defined in ADR-001. Bumped according to semver:- Major: breaking change to
BasePluginABC,PluginMetaschema, or any model in the platform contract (Asset,AssetType,AssetStatus,AssetRelationship,Vendor,Location,BusinessUnit,Model,OperatingSystem). - Minor: additive change (new optional hook, new field on a contract model with default).
- Patch: bug fix, no contract surface change.
- Major: breaking change to
- Plugin requirement (
plugins/<name>/manifest.json): the existingcore_versionfield, expressed as a semver range (e.g.,">=1.0.0,<2.0.0").
The plugin loader (shopdb/plugins/loader.py) checks core_version against __contract_version__ at load time. Mismatch in dev = re-raise (fail loud). Mismatch in prod = log error, mark plugin as incompatible, exclude from registration.
The __contract_version__ starts at 1.0.0 when ADR-001 is accepted and the Machine retirement migration is complete (whichever comes later). Until then, the framework is pre-1.0; plugins should declare core_version: ">=0.1.0,<1.0.0".
Consequences
Positive
- Sister sites can pin a known-good framework version. They will not be silently broken when the framework is upgraded.
- Plugin authors know what counts as a breaking change because the contract surface is enumerated in ADR-001.
- The loader fails predictably: a mismatched plugin is reported, not silently disabled.
Negative / cost
- Discipline required: every change to the contract surface must be classified (major / minor / patch). Adding a
version-bumpskill (or a check in code review) reduces the chance of mis-classification. __contract_version__becomes a coupling point. Forgetting to bump it after a breaking change means downstream plugins crash silently at runtime instead of failing at install.
Neutral
- Existing plugins (
plugins/printers/, etc.) ship as part of the framework, so theircore_versionis always the current__contract_version__. The discipline matters mostly for external / sister-site plugins.
Alternatives considered
- No versioning, just trust. Works for an in-tree-only world. Fails the moment a sister site ships its own plugin. Rejected.
- Calendar versioning (e.g.,
2026.05.0). Easier to bump, harder to communicate breaking changes. Rejected; semver is the industry standard for library-like contracts. - Per-hook versioning. Each hook has its own version. Too granular; plugins still couple to multiple hooks. Rejected.
Open questions
- When does the framework declare
1.0.0? Tied to ADR-001 (Asset retirement of Machine) and the framework being deemed "ready for sister sites". Best-effort target: end of Phase 5 in the refactor plan. - Should
core_versionaccept commercial-grade ranges (^1.0.0) or stick to PEP 440 / npm-style ranges? Recommend pip-style (>=,<) to match Python ecosystem.
References
shopdb/plugins/base.py(PluginMeta declaration)shopdb/plugins/loader.py(where the version check belongs)- ADR-001 (defines what is in the contract)
Docs
Install and operate
Data import
Plugins
Integrations
Project
ADRs
- ADR-001-asset-as-platform-contract
- ADR-002-plugin-versioning
- ADR-003-plugin-distribution
- ADR-004-deployment-topology
- ADR-005-equipment-vs-measuringtools
- ADR-006-collector-contract
- ADR-007-product-versioning-and-releases
- ADR-008-plugin-migration-ownership
- ADR-009-frontend-plugin-gating
- ADR-010-frontend-plugin-hooks
- ADR-011-machines-rename
- ADR-012-geenforce-manifest-ownership
- README
Proposals