Formal stability contract for the
1.xline. Binding — any release that violates it is a bug.
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 undererror_forge::{console_theme, context, collector, error, logging, macros, recovery, registry}(andasync_error/async_error_implunderfeature = "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_hookfromAppError/define_errors!constructors. ForgeError::registercalling the same hook (and the documented double-fire if both paths are exercised).define_errors!generating lowercase constructors andDisplay/Error/ForgeErrorimpls.group!requiring each wrapped type to implementForgeError.
The 1.x line follows Semantic Versioning 2.0.0.
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.
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.
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
[]in1.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.0migration indocs/migration.md.
A symbol marked #[deprecated] in a 1.x.Y release:
- Remains callable for the entire
1.xline. - Continues to behave per its documented contract.
- May only be removed in a
2.0.0release.
Items deprecated in 1.0.0:
error_forge::macros::register_error_hook(non-tryvariant). Usetry_register_error_hookinstead. The non-tryvariant silently discardsErr; thetry_variant returns it.error_forge::Result<T>(the type alias). Useerror_forge::AppResult<T>instead. The unqualifiedResultname shadowsstd::result::Resultinuse error_forge::*glob imports.
Both deprecated names remain callable through the entire 1.x
line and are removed only in 2.0.0.
The crate panics in exactly one place by design: hook registration is per-process, and a corrupt registry state would be a bug. Specifically:
AppErroranddefine_errors!constructors never panic through normal use.ForgeErrordefault methods never panic.register_error_hook/try_register_error_hooknever panic on duplicate registration; thetry_variant returnsErr("Error hook already registered")instead.ConsoleThemeformatting never panics; ANSI escapes are static strings.recovery::CircuitBreakerusesparking_lot::Mutex, which does not poison;lock()cannot panic in our usage.recovery::RetryExecutor::retrypropagates the inner operation's panic if the closure panics (matches what every retry library does).
The stability contract does NOT cover:
- Exact wording of error messages and panic-payload messages.
The error kind (visible through
ForgeError::kind()), theDisplaychain viaError::source, and the prefix"[CODE] "onCodedError-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 inerror.rs::panic_payload_to_listener_error, the privaterecovery::retry::{BackoffStrategy, BackoffType, RetryPredicate}) is internal and may move or change between minor releases. - Performance characteristics. A
1.x.Ymay make any operation faster or slower than1.0.0. Documented benchmark numbers (where they exist) are illustrative, not contractual.
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 archivedpaste).parking_lot(always-on, non-poisoningMutexused byrecovery::CircuitBreaker).error-forge-derive(optional, gated onderivefeature).serde(optional, gated onserdefeature).log(optional, gated onlogfeature).tracing(optional, gated ontracingfeature).async-trait(optional, gated onasyncfeature).rand(optional, gated onjitterfeature).
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.
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:
io::Error::other(used byAppError::filesystem) is stable since1.74.- The committed
Cargo.lockis format v4, which Cargo cannot parse on toolchains older than1.78. - Clippy's
incompatible_msrvlint flags additional1.81items in the current source.
1.81 is the conservative floor satisfying all three.
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.
If you encounter what looks like a 1.x stability break:
- Run the failing case against the latest patch release of
1.xto confirm reproducibility. - Capture the exact API call sequence and observed-vs-expected behaviour.
- 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.