API stability and deprecation policy
August 28, 2026 ยท View on GitHub
This document is the compatibility contract for base-cli. It applies to the
current pre-1.0 releases and will be updated before the 1.0 release. The
repository's contract tests and release checklist are expected to change with
this document, so a policy change is itself a reviewed, changelogged change.
Public surface
The supported Python facade is import base_cli. The names in
base_cli.__all__ are the public facade; documented names in the explicitly
exported modules (base_cli.command_protocol, base_cli.json_contracts, and
the other modules listed in their module-level __all__) are public as well.
Names beginning with _, modules not documented here, and implementation
details are private and may change without notice.
The following machine-facing contracts are public and versioned:
- command framing defaults to
COMMAND_PROTOCOL_V1; record schemas are owned and registered by the consumer; - JSON output, error, and log records use
schema_version: 1and the schema identifiers documented injson-contracts.md; and - the exported callable and type signatures exercised by the public API tests.
Human-readable messages, table layout, log wording, temporary directory names, and history implementation details are not stable machine interfaces unless a separate contract document says otherwise. Consumers that need automation should select the JSON or record protocol contracts.
The importable base_cli.experimental module is reserved for preview APIs. No
experimental symbols are currently shipped. A future preview must live under
that namespace, be labelled experimental in its documentation, and must not
be re-exported from the stable facade until it is promoted.
Versioning and compatibility
Starting with 1.0, base-cli follows Semantic Versioning:
- a MAJOR release may remove or change public APIs and versioned contracts;
- a MINOR release adds backwards-compatible functionality and may begin a deprecation; and
- a PATCH release contains compatible fixes, security fixes, and docs.
Until 1.0, the leading zero is meaningful: patch releases remain compatible,
while a minor release is a compatibility boundary and may contain a breaking
change. We still prefer additive changes, and any pre-1.0 break must include a
warning where practical, a migration path, and a changelog entry. Consumers
that need a frozen API should pin a minor release (for example, ~=0.4.0).
The core package requires Python >=3.10 and currently tests CPython 3.10
through 3.14 on Linux, macOS, and Windows. The core runtime dependency is
Click >=8.1,<8.5; the compatibility suite covers the 8.1, 8.2, 8.3, and
8.4 lines on Python 3.10 and 3.14. YAML configuration and YAML output are provided by the
optional base-cli[yaml] extra, which supplies PyYAML >=6.0,<7. Other
optional integrations are independently versioned and constrained in pyproject.toml: Typer
>=0.12,<0.28, Rich >=13.7,<15, and OpenTelemetry API >=1.24,<2. The
lower bounds are the minimum supported versions; a dependency minor or major
release is supported only after it passes the compatibility suite. The tested core
dependency matrix is maintained in dependency-support.md.
A future minor release may drop an end-of-life Python or dependency window with
a migration note.
Platform tier details and the operating-system support test matrix are kept in
platform-support.md.
Deprecation process
Use the public base_cli.deprecated decorator for callable APIs:
from base_cli import deprecated
@deprecated("0.3", remove="1.0", alternative="new_name")
def old_name(value: str) -> str:
return new_name(value)
The decorator preserves the callable's metadata and behavior, and emits a
BaseCliDeprecationWarning with stacklevel=2 on every call. Applications can
show or fail on these warnings with the standard warnings filters. The
warning identifies the release that introduced the deprecation, the planned
removal release, and (when available) the replacement API.
Every deprecation must:
- remain supported for at least two minor releases and 90 calendar days, whichever is longer;
- include a migration note in
migrations.mdor the relevant contract document; - appear in
CHANGELOG.mdunder the release that introduces the warning and the release that removes the API; and - be removed only in the stated removal release (or a later release), except for an urgent security or legal fix that is explicitly documented.
The removal PR must delete the contract test for the old symbol only after the replacement and migration guidance are present. A deprecation is not complete until the warning, docs, tests, and changelog agree.
Contract guardrails
tests/test_public_api.py verifies that every facade export is resolvable,
that module __all__ surfaces do not silently drift, and that private legacy
names stay absent. tests/test_api_stability.py additionally verifies the
warning behavior, the default command-protocol header, and the JSON v1 schema
identifiers and envelope fields. Changes to an exported symbol or a versioned
schema therefore require an intentional test and documentation update before
release.
When a schema must evolve incompatibly, add a new version and an adapter rather than changing the meaning of an existing field in place. Keep the old version available for the same deprecation window and document the migration.
Release checklist
Before publishing a release, maintainers should confirm:
- the supported Python and dependency windows still match CI and
pyproject.toml; - public exports and schema constants have contract-test coverage;
- each deprecation has a warning, removal target, migration note, and changelog entry; and
- the release notes call out any pre-1.0 compatibility boundary or contract version addition.
See releasing.md for the mechanical package and publishing
steps.