Skip to main content

Compatibility policy

Nucleus governs three stable surfaces by contract:

  1. Public Go API — exported symbols in pkg/*.
  2. CLI — registered commands, flags, and the JSON shape of their structured output.
  3. Configuration — registered keys in nucleus.yml.

Each surface is pinned by automated tests under contracts/. The freeze tests refuse to remove an entry without a deprecation record.

Pre-1.0 vs. v1.x

PeriodWhat you can rely on
pre-1.0 (v0.x)The contracts exist and prevent silent regressions, but minor versions may make documented breaking changes. Read CHANGELOG.md between upgrades.
v1.xThe full Compatibility SLO applies. See docs/governance/COMPATIBILITY_SLO.md.

The clock on the SLO starts at v1.0. We do not maintain a parallel SLO during pre-1.0 — that would be a promise we cannot keep while the framework's core is still consolidating.

What "frozen" means

For each surface, the corresponding baseline lives under contracts/baseline/:

BaselineWhat it pins
api_exported_symbols.txtThe list of exported symbols in pkg/*.
cli_primary_commands.txtThe list of stable CLI commands.
cli_json_status_keys.txtThe JSON keys returned by status commands.
config_key_patterns.txtThe valid nucleus.yml key paths.

The freeze tests fail loudly on any net removal. The baselines are never edited to make a test pass — they are regenerated only as part of an explicit, documented contract change.

How a deprecation works

A deprecation goes through three stages:

  1. Marked — the symbol / command / key is annotated as deprecated; it still works. A row is added to docs/governance/DEPRECATION_TEMPLATE.md with the planned removal release.
  2. Warned — the runtime / CLI emits a structured warning when the deprecated entry is used.
  3. Removed — at the planned release. The freeze test now passes without the entry. The CHANGELOG records the removal under the appropriate version heading.

A migration assistant accompanies any v1.x → v(1+N) deprecation that affects user code, per docs/governance/MIGRATION_ASSISTANT_CONVENTIONS.md.

Type firewall

Critical third-party dependencies are kept behind framework interfaces. A dedicated test (contracts/firewall_test.go) AST-parses pkg/* and fails if a non-stdlib type leaks into a stable signature.

That is what makes the SQL-driver swap drill possible: switching from SQLite to PostgreSQL to MySQL is a config change, not a code change, because no *sql.DB-adjacent third-party type appears in any exported signature.

Reading the registry

Rather than reading the contract baselines directly, three documents in docs/reference/ are kept canonical:

These are kept in lockstep with the baselines by the iteration workflow described in CLAUDE.md.