Files
shopdb-flask/docs/CONTRACT-STABILITY.md
cproudlock 529b9f2fed
All checks were successful
CI / backend (push) Successful in 24s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 7s
Ship plugin framework shore-up: frontend scaffold, sister-site adoption kit
- flask plugin new now scaffolds the frontend too: List/Detail/Form
  views on the global styles, a gated route module (ADR-009), and an
  api-client snippet emitted into the plugin dir. Views are written
  before the route file so a partially generated plugin cannot 500 the
  dev server.
- docs/PLUGIN-EXTERNAL-REPO.md + scripts/test-external-plugin.sh: how a
  sister site develops a plugin in its own repo and runs the framework
  contract tests in CI against a pinned framework ref (script verified
  to fail on a broken core_version pin).
- docs/CONTRACT-STABILITY.md: settled vs churning contract surface and
  the provisional 1.0 criteria.
- CLAUDE.md active-state refresh (contract 0.6.0, 11 plugins, 340
  tests, measuringtools done).

Known limitation documented: Path.rglob does not descend symlinks, so
the import-surface contract test skips symlinked external plugins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:30:03 -04:00

6.6 KiB

Path to contract 1.0

This page is an honest read of how stable the plugin contract is today, for a sister site deciding how much to build on it. It is derived from the ADRs and the live code, not aspiration. The authoritative hook reference is PLUGIN-HOOKS.md; the versioning rules are in ADR-002.

Current version

The plugin contract is at 0.6.0, declared in shopdb/__init__.py as __contract_version__. It is pre-1.0, which under semver means any 0.x minor bump is allowed to break the contract, and this project has used that latitude.

The product release version (__version__, currently 0.5.0) is a separate series with its own bump rules; see ADR-007. Do not pin against it for compatibility - pin against __contract_version__.

0.x history

Recorded in the comment block in shopdb/__init__.py:

Version Change Kind
0.3.0 shopdb.api expanded to the full plugin import surface (db, cache, model bases, core models, response + pagination helpers, employee_connection) so plugins stop importing internal core paths additive (minor)
0.4.0 Removed the never-implemented get_searchable_fields hook (search is a core concern over the asset model) and wired get_dashboard_widgets to a real consumer (/api/dashboard/widgets) pre-1.0 contract reduction
0.6.0 Added the get_reports hook, consumed by GET /api/reports to merge plugin report cards into the Reports hub additive optional hook (minor)

The source comment block documents 0.3.0, 0.4.0, and 0.6.0. Earlier points (0.1.x / 0.2.x) predate that recorded rationale; PluginMeta's fallback core_version default of >=0.2.0,<1.0.0 is the only remaining trace of the 0.2 baseline.

Settled surface

These are unlikely to break before 1.0. A plugin can depend on them with reasonable confidence; a breaking change to any would be a major bump and would land with a new or amended ADR.

Surface What it is
meta / manifest schema PluginMeta fields and manifest.json (ADR-002 single source of truth)
get_blueprint Flask Blueprint registration at the manifest api_prefix
get_models SQLAlchemy model classes the plugin owns
init_app Custom init after blueprint + models are known
get_cli_commands Click commands added to the Flask CLI
get_services Named service classes, consumed by plugin_manager.get_service
Lifecycle hooks on_install, on_uninstall, on_enable, on_disable
get_navigation_items Sidebar menu entries
get_dashboard_widgets Dashboard widgets, consumed by /api/dashboard/widgets
get_reports Report cards, consumed by /api/reports (added 0.6.0)
Collector pair get_collector_schema + apply_collector_payload per ADR-006
Settings helpers get_setting / set_setting, namespaced to the plugin
get_provisioning_note Setup-wizard transparency note for extra tables
get_config_schema Setup-wizard config field declarations
shopdb.api import surface The only core module plugins may import (plus shopdb.plugins.base); adding a name is minor, removing one is major

The asset model itself - Asset, AssetType, AssetStatus, AssetRelationship, and the shared reference models - is the platform contract locked in ADR-001.

Expected churn before 1.0

Known-unstable areas. Building on these means expecting rework.

Area Status Reference
Frontend hook contract Not defined yet. There is no server-side hook for asset-detail panels, map markers, or search-result rendering. A plugin that needs custom UI still hand-edits the Vue frontend. This is the single biggest gap. project charter / this doc
Per-plugin migrations Brand new. The per-plugin Alembic engine exists and every bundled plugin now carries a chain, but the pattern has one release of production mileage, not years. ADR-008 (2026-07-10)
Pip distribution Deferred to v2. External plugins install by clone / submodule / symlink; there is no entry-point discovery and no automatic update path yet. ADR-003

Bump rules

From ADR-002, applied to __contract_version__:

Bump Trigger
major Breaking change to the BasePlugin ABC, the PluginMeta schema, or any model in the platform contract (Asset, AssetType, AssetStatus, AssetRelationship, Vendor, Location, BusinessUnit, Model, OperatingSystem). Removing a name from shopdb.api is major.
minor Additive change: a new optional hook, a new field on a contract model with a default, a new name added to shopdb.api.
patch Bug fix with no change to the contract surface.

Deprecation policy

ADR-002 defines the bump classification above but is silent on any deprecation window or notice period for pre-1.0 removals. In practice a removal is simply a major (or, pre-1.0, a breaking minor) bump: the hook or name is gone, and the loader fails loud in dev or excludes the mismatched plugin in prod (see the load-time table in PLUGIN-EXTERNAL-REPO.md). Because the ADR says nothing about a grace period, this document proposes none; the honest guidance for sister sites is the mitigation that already exists - pin a tight core_version range and re-test before widening it.

Criteria for declaring 1.0

Provisional. This is the maintainer's working list, not a committed checklist, and it will move. ADR-002's own open question ("When does the framework declare 1.0.0?") ties 1.0 to the Machine retirement from ADR-001 and the framework being "ready for sister sites"; the items below expand that intent.

  • Frontend hook contract defined via its own ADR (asset-detail panels, map markers, search results), closing the biggest churn item above.
  • Machine / legacy-model retirement complete per ADR-001, so the asset model is the only contract.
  • At least two external plugins running in production at a second site - the bar ADR-003 already sets before pip distribution and sister-site readiness are considered justified.
  • The per-plugin migration pattern (ADR-008) proven across real upgrades, not just fresh installs.
  • No contract bump needed for N consecutive product releases (N to be fixed when the list is firmed up), showing the surface has actually settled.

Until then: pre-1.0, pin tight, re-test on every framework bump.