Skip to content

Latest commit

 

History

History
213 lines (167 loc) · 7.58 KB

File metadata and controls

213 lines (167 loc) · 7.58 KB

error-forge — Stability and SemVer Policy

Formal stability contract for the 1.x line. Binding — any release that violates it is a bug.

Headline guarantee

Every public symbol in 1.0.0 is locked for the entire 1.x line. Signatures, observable behaviour, and the documented contract of every method cannot change without a major version bump to 2.0.0.

This applies to:

  • Every symbol re-exported by mod error_forge (the crate root) and the per-module re-exports under error_forge::{console_theme, context, collector, error, logging, macros, recovery, registry} (and async_error / async_error_impl under feature = "async").
  • The trait implementations on the public types (std::error::Error, std::fmt::Display, std::fmt::Debug, Clone, From<T>).
  • The documented behavioural contracts of every pub fn, including:
    • Panic-vs-error semantics.
    • Auto-firing of call_error_hook from AppError / define_errors! constructors.
    • ForgeError::register calling the same hook (and the documented double-fire if both paths are exercised).
    • define_errors! generating lowercase constructors and Display / Error / ForgeError impls.
    • group! requiring each wrapped type to implement ForgeError.

Version policy

The 1.x line follows Semantic Versioning 2.0.0.

Patch releases (1.x.Y)

May contain:

  • Bug fixes that do not change observable output for any documented input.
  • Documentation improvements.
  • Internal performance work that does not move any public surface.
  • Test additions.
  • Internal dependency updates within their compatible range.

May NOT contain:

  • New public items.
  • Behavioural changes (including panic-message wording when callers may parse it, hook-firing semantics, etc.).
  • MSRV bumps.

Minor releases (1.Y.0)

May contain everything a patch release may contain, plus:

  • New public items (functions, methods, types, modules) so long as they are pure additions — no signature change to any existing symbol.
  • New optional cargo features that default to off, or default-on features that activate purely additive code paths.
  • New variants on #[non_exhaustive] enums (ErrorLevel, CircuitState) or new fields on #[non_exhaustive] structs (ErrorContext, ErrorCodeInfo, CodedError, ContextError, CircuitBreakerConfig).
  • MSRV bumps. Each MSRV bump is a minor-version bump minimum and is called out in the CHANGELOG.

Major releases (2.0.0)

Required for any change that violates the above. Specifically, any of the following requires 2.0.0:

  • Removal or rename of any public symbol.
  • A signature change to any existing public symbol (including loosening or tightening the panicking conditions in a user-observable way).
  • Removing a #[non_exhaustive] marker (which would let external code resume struct-literal construction — considered a contract narrowing, not a widening).
  • Changing the default-feature set (default features are [] in 1.0.0; adding a default feature, or making a default feature opt-in, is breaking).
  • Adding a runtime dependency that is not gated behind an opt-in feature.
  • A change that breaks the documented 0.9.x → 1.0.0 migration in docs/migration.md.

Deprecation policy

A symbol marked #[deprecated] in a 1.x.Y release:

  • Remains callable for the entire 1.x line.
  • Continues to behave per its documented contract.
  • May only be removed in a 2.0.0 release.

Items deprecated in 1.0.0:

  • error_forge::macros::register_error_hook (non-try variant). Use try_register_error_hook instead. The non-try variant silently discards Err; the try_ variant returns it.
  • error_forge::Result<T> (the type alias). Use error_forge::AppResult<T> instead. The unqualified Result name shadows std::result::Result in use error_forge::* glob imports.

Both deprecated names remain callable through the entire 1.x line and are removed only in 2.0.0.

Panic safety

The crate panics in exactly one place by design: hook registration is per-process, and a corrupt registry state would be a bug. Specifically:

  • AppError and define_errors! constructors never panic through normal use.
  • ForgeError default methods never panic.
  • register_error_hook / try_register_error_hook never panic on duplicate registration; the try_ variant returns Err("Error hook already registered") instead.
  • ConsoleTheme formatting never panics; ANSI escapes are static strings.
  • recovery::CircuitBreaker uses parking_lot::Mutex, which does not poison; lock() cannot panic in our usage.
  • recovery::RetryExecutor::retry propagates the inner operation's panic if the closure panics (matches what every retry library does).

What is NOT covered

The stability contract does NOT cover:

  • Exact wording of error messages and panic-payload messages. The error kind (visible through ForgeError::kind()), the Display chain via Error::source, and the prefix "[CODE] " on CodedError-formatted output are part of the contract; the human-readable text following them is not.
  • Internal types and modules. Any item not re-exported through lib.rs (pastey, internal helpers in error.rs::panic_payload_to_listener_error, the private recovery::retry::{BackoffStrategy, BackoffType, RetryPredicate}) is internal and may move or change between minor releases.
  • Performance characteristics. A 1.x.Y may make any operation faster or slower than 1.0.0. Documented benchmark numbers (where they exist) are illustrative, not contractual.

Dependencies

The following runtime dependencies are sealed for the 1.x line:

  • thiserror (always-on, error-handling support).
  • pastey (always-on, macro support — drop-in fork of the archived paste).
  • parking_lot (always-on, non-poisoning Mutex used by recovery::CircuitBreaker).
  • error-forge-derive (optional, gated on derive feature).
  • serde (optional, gated on serde feature).
  • log (optional, gated on log feature).
  • tracing (optional, gated on tracing feature).
  • async-trait (optional, gated on async feature).
  • rand (optional, gated on jitter feature).

Adding a new runtime dependency requires a 2.0.0 bump. Removing any of the always-on deps requires a 2.0.0 bump. Adding new optional dependencies behind a new opt-in feature is a minor-version bump.

MSRV

1.0.0 ships with MSRV 1.81.0. Any change to the MSRV is a minor-version bump minimum.

The 1.81 floor is driven by three independent constraints:

  1. io::Error::other (used by AppError::filesystem) is stable since 1.74.
  2. The committed Cargo.lock is format v4, which Cargo cannot parse on toolchains older than 1.78.
  3. Clippy's incompatible_msrv lint flags additional 1.81 items in the current source.

1.81 is the conservative floor satisfying all three.

Feature flags

The default feature set is [] — every optional feature is opt-in. The full feature list is documented in docs/API.md.

Removing a feature from defaults is fine (no change since the default is empty). Adding a default feature is breaking. Renaming a feature is breaking.

Reporting a stability break

If you encounter what looks like a 1.x stability break:

  1. Run the failing case against the latest patch release of 1.x to confirm reproducibility.
  2. Capture the exact API call sequence and observed-vs-expected behaviour.
  3. Open an issue at https://github.com/jamesgober/error-forge/issues with the repro.

Stability breaks are bugs and are fixed in the next patch release.