error-forge 1.0.0

Pragmatic Rust error-handling framework with stable error metadata, contextual diagnostics, optional async support, and synchronous recovery primitives (retry, circuit-breaker, backoff). Optional #[derive(ModError)], declarative define_errors!, and feature-gated logging / tracing / serde adapters.
Documentation
# Changelog

All notable changes to error-forge will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.0.0] - 2026-05-18

Stable API. The public surface is locked under SemVer for the entire `1.x` line — see [`docs/STABILITY.md`](docs/STABILITY.md) for the binding policy and [`docs/API-FREEZE-AUDIT.md`](docs/API-FREEZE-AUDIT.md) for the surface manifest. Three breaking corrections at the freeze boundary (`group!` macro, `parking_lot::Mutex` in `CircuitBreaker`, `AsyncForgeError::async_handle` default), several deprecations, `#[non_exhaustive]` annotations on public types likely to grow, and a substantial documentation expansion. See the full release notes in [`.dev/release/v1.0.0.md`](.dev/release/v1.0.0.md).

### Added

- **`docs/STABILITY.md`** — formal SemVer / panic-safety / determinism / deprecation / dependency / MSRV policy for the `1.x` line.
- **`docs/API-FREEZE-AUDIT.md`** — manifest of every public symbol in `1.0.0`, what trait impls it carries, what is `pub(crate)`, what is intentionally out of scope.
- **`docs/COMPARISON.md`** — honest side-by-side with `anyhow`, `thiserror`, `miette`, `snafu`, `eyre` including non-strengths.
- **`docs/architecture.md`** — design rationale (why `ForgeError` trait, why sync recovery, why `parking_lot`, etc.).
- **`docs/migration.md`** — upgrade guide from `0.9.x → 1.0.0` (and historical `0.6.x → 0.9.x → 1.0.0` summary).
- **`SECURITY.md`** — vulnerability disclosure policy with the `me@jamesgober.com` private reporting channel and triage timeline.
- **`CONTRIBUTING.md`** — contributor workflow, CI gates, code style, banned terms.
- **`RELEASING.md`** — codified release procedure with the derive-first publish order documented.
- **`CODEOWNERS`** — single-maintainer default.
- **`.dev/DIRECTIVES.md`** and **`.dev/ROADMAP.md`** — internal workflow rules and outstanding-work tracker (git-ignored).
- **`.github/dependabot.yml`** — weekly grouped updates for `cargo` and `github-actions` ecosystems with the `dtolnay/rust-toolchain` tag-format workaround.
- **`.github/PULL_REQUEST_TEMPLATE.md`** + `.github/ISSUE_TEMPLATE/{bug_report,feature_request}.md`.
- **`deny.toml`** — license allow-list, wildcard ban, `crates.io`-only sources.
- **`cargo deny` CI job** alongside `cargo audit`.
- **`new()` constructors on `ErrorContext` and `CircuitBreakerConfig`** so external callers (tests, custom wiring) can build these `#[non_exhaustive]` types without depending on the field list.
- **`CircuitBreakerConfig::with_failure_threshold` / `with_failure_window_ms` / `with_reset_timeout_ms`** builder methods for partial overrides.
- **`AppResult<T>`** — new type alias for `Result<T, AppError>` that does not shadow `std::result::Result` in glob imports.

### Changed

- **Breaking — `group!` macro rewritten.** The internal `@with_impl` arm had a macro-parse ambiguity in `0.9.x` that prevented top-level invocations of the documented form (covered by `ignore`d doctest in `0.9.x`). The rewrite eliminates the second repetition block entirely, and the generated `ForgeError` impl now delegates directly to each wrapped type's `ForgeError` methods instead of the silently-broken type-erased downcast pattern. **Every wrapped variant must now implement `ForgeError`** — wrap foreign types in `define_errors!` or `#[derive(ModError)]` enums first. Doctest is no longer `ignore`d and the parse-ambiguity is gone.
- **Breaking — `CircuitBreaker` now uses `parking_lot::Mutex` instead of `std::sync::Mutex`**. Removes the lock-poisoning hazard that `0.9.x` shipped — a panic inside an `execute(|| ...)` closure no longer poisons the breaker for the rest of the process. Adds `parking_lot` to the runtime deps (always-on).
- **Breaking — `AsyncForgeError::async_handle` gains a default no-op body.** Was a required method in `0.9.x` with a stub `Ok(())` impl on `AppError`; the stub is removed. Implementors override `async_handle` only if they want async behaviour.
- **Breaking — `AppError::with_async_context` removed.** The method took a `FnOnce()` and synchronously called it; nothing async about it. Use `ResultExt::with_context` or build `ContextError` directly.
- **Breaking — `#[non_exhaustive]` added to `ErrorLevel`, `ErrorContext`, `ErrorCodeInfo`, `CodedError`, `ContextError`, `CircuitBreakerConfig`, `CircuitState`.** External code can no longer struct-literal-construct these types, and exhaustive `match` statements need a wildcard arm. Migration guide in [`docs/migration.md`](docs/migration.md).
- **Breaking — `CodedError::new` no longer auto-registers the code** in the global registry. Pre-register codes at startup with `register_error_code` if you want documentation URLs or per-code retryability. Hot-path cost on `with_code(...)` is now one allocation and zero locking.
- **Breaking — hook callback widened to `Box<dyn Fn(ErrorContext<'_>) + Send + Sync + 'static>`.** Was a bare function pointer `fn(ErrorContext)` in `0.9.x`; the new shape accepts closures capturing thread-safe state.
- **Breaking — `pub use crate::macros::*;` narrowed.** The wildcard re-exported `ErrorContext`, `ErrorLevel`, `ErrorSource`, `register_error_hook`, `try_register_error_hook`, `call_error_hook`. `1.0` lists the first five explicitly and drops `call_error_hook` from the prelude path (still reachable via `error_forge::macros::call_error_hook`).
- **Performance — `CodedError::new` no longer takes a registry lock** (see Breaking note above).

### Deprecated

- **`error_forge::macros::register_error_hook`** (non-`try` variant). Silently discards the registration failure when a hook is already installed. Use `try_register_error_hook` instead, which returns `Err("Error hook already registered")` so callers can decide how to react.
- **`error_forge::Result<T>`** type alias. Shadows `std::result::Result` in `use error_forge::*` glob imports — a quiet footgun in caller code. Use `error_forge::AppResult<T>` instead.

Both deprecations stay callable through the entire `1.x` line; they are removed only in `2.0`.

### Security

- **`Cargo.lock` updated** to clear two RustSec advisories surfaced by `cargo-deny check`:
  - **GHSA-434x-w66g-qw3r / bytes 1.10.1** (integer overflow in `BytesMut::reserve`). Pulled patched `bytes 1.11.1` into the lockfile. Transitive via `tokio` (dev-only — does not affect consumers).
  - **RUSTSEC-2026-0097 / rand 0.8.5** (unsoundness in `rand::rng()` under a custom-logger + reseed scenario). Pulled patched `rand 0.8.6` into the lockfile. Only pulled in under the optional `jitter` feature; the unsoundness conditions do not apply to `error-forge`'s usage, but updating clears the advisory regardless.

### Compatibility

- MSRV stays at Rust `1.81`. No bump.
- The default feature set is `[]` (every optional feature is opt-in). Unchanged.
- New optional `jitter` feature gates the `rand` runtime dep that previously was always-on. Existing users who do not call `ExponentialBackoff::with_jitter(true)` save the `rand` transitive deps; users who do, add `features = ["jitter"]` to their `Cargo.toml`.

## [0.9.8] - 2026-05-18

Cleanup release. Drops the deprecated `atty` dependency, prunes a dead feature flag, tightens `ConsoleTheme` allocations, resolves three of the four ignored doctests, and rewrites the `Cargo.toml` description without banned terms. No public API changes. Sets `rust-version` in `Cargo.toml` for the first time and ships the corresponding MSRV-verification CI job.

### Added
- `rust-version = "1.81"` in `Cargo.toml`. MSRV is now declared explicitly and CI runs a dedicated MSRV-pin job verifying the crate builds against the exact `1.81.0` toolchain. The floor is driven by three independent constraints: `io::Error::other` (used by `AppError::filesystem` builders) is stable since 1.74; clippy's `incompatible_msrv` lint flags additional 1.81 items in the current source; and the committed `Cargo.lock` is format v4, which Cargo cannot parse on toolchains older than 1.78. `1.81` is the conservative floor that satisfies all three and matches `mod-events` in the workspace for consistency.
- `MSRV` badge in `README.md`.
- `cargo audit` CI job that runs the RustSec advisory database against every push. The job declares `permissions: { contents: read, checks: write }` so the `rustsec/audit-check` action can post findings as GitHub Check Runs without hitting `Resource not accessible by integration`.
- `#[doc(hidden)] pub mod __private` in `lib.rs` re-exporting `pastey` so that `define_errors!` expansions in downstream crates do not require the user to add the dep manually.
- `.dev/release/v0.9.8.md` release note describing the full cleanup scope.

### Changed
- **`Cargo.toml` description rewritten without banned terms** (`comprehensive`, `robust`). The new text reads: "Pragmatic Rust error-handling framework with stable error metadata, contextual diagnostics, optional async support, and synchronous recovery primitives (retry, circuit-breaker, backoff). Optional `#[derive(ModError)]`, declarative `define_errors!`, and feature-gated logging / tracing / serde adapters."
- **`ConsoleTheme` colour fields are now `&'static str`** instead of `String`. Saves 8 heap allocations per `ConsoleTheme::default()` / `with_colors()` / `plain()` construction. `with_colors()` and `plain()` are now `const fn`.
- **`print_error` caches its default `ConsoleTheme`** in a process-wide `OnceLock`. The terminal-capability check runs at most once regardless of how many errors are printed.
- **`ConsoleTheme::format_error` writes into a single `String` buffer** using `std::fmt::Write` instead of allocating an intermediate string per section. Allocations drop from ~6 to 1 per error formatted.
- **`terminal_supports_ansi()` decision is now cached in a `OnceLock`** on every platform (the Windows branch already did this; the Unix branch did the env-var checks on every call).
- README `MSRV` badge added; install snippet bumped from `0.9.7` to `0.9.8` and now documents the `1.70` MSRV. Per-feature install lines unchanged.
- `lib.rs` doctests for the crate-level Quick Start and Built-in Formatting blocks no longer carry the `ignore` annotation — they compile and run as part of `cargo test --all-features`.
- `recovery/mod.rs` module-level doctest now compiles and runs (the closure return type is declared so the retry policy can type-check the loop).
- `async_error.rs::AsyncForgeError` doctest is now gated behind a hidden `#[cfg(feature = "async")]` block and runs under `cargo test --all-features`.
- `group_macro.rs::group` doctest remains `ignore`d but now documents two known limitations explicitly (a macro-parse ambiguity in `@with_impl` and a broken `ForgeError` delegation pattern). Both are scheduled to be fixed in `1.0`.
- CHANGELOG `[0.6.3]` entry no longer carries the "Current release version" marker (`0.6.3` is well past current).

### Fixed
- **Replaced `atty` with `std::io::IsTerminal`.** `atty` is unmaintained and on the RustSec radar (RUSTSEC-2021-0145 — unaligned-read race). `std::io::IsTerminal` (stable since Rust 1.70) is the canonical replacement and lets `error-forge` drop the dependency entirely. Affects `console_theme::terminal_supports_ansi`; behaviour is unchanged from the user's perspective.
- **Replaced `paste` with `pastey`.** `paste 1.0.15` is unmaintained as of 2024-10-07 (RUSTSEC-2024-0436 — the author archived the repo). `pastey 0.2.2` is the published drop-in fork: identical `paste!` macro semantics, same `[<...>]` token-pasting and case-modifier syntax that `define_errors!` relies on. `cargo audit` now reports zero warnings on the committed `Cargo.lock`.
- **Audit CI job permissions.** Added `permissions: { contents: read, checks: write }` to the `audit` job so the `rustsec/audit-check` action's Check Run posting no longer fails with `Resource not accessible by integration`.

### Removed
- **`atty` dependency.** No longer needed after the `IsTerminal` migration.
- **`paste` dependency.** Replaced by `pastey`; see Fixed section.
- **`once_cell` dependency and the `thread-safety` feature flag** that gated it. `once_cell::sync::OnceCell` was never imported anywhere in the crate — every `OnceLock` usage was already `std::sync::OnceLock` (stable since `1.70`). The `thread-safety` feature was dead code; enabling it added a dep with no behaviour change.
- **`extern crate serde;`** from `lib.rs`. This Rust 2015 idiom is unnecessary in 2021 edition and provided no functional value.
- **Placeholder `it_works` test from `lib.rs`** (`assert_eq!(2 + 2, 4);`). The two remaining inline tests (`test_error_display`, `test_error_kind`) carry actual coverage; placeholder cruft is gone.

### Compatibility
- No public API changes from `0.9.7`. The `Cargo.toml` `version` bump, `rust-version` declaration, and dependency-table shrinkage are the only consumer-visible deltas. Existing source compiles unchanged.
- Removing the `thread-safety` feature is technically a breaking change for any consumer that lists it explicitly, but the feature gated zero behaviour, so no observable consequences.
- MSRV is now formally `1.81` (driven by `io::Error::other` + lockfile format v4 + clippy `incompatible_msrv` lint findings; see the `Added` section for the full rationale). A dedicated MSRV CI job verifies this on every push.

## [0.9.7] - 2026-03-24

### Added
- Added `try_register_error_hook(...)` so applications can detect duplicate hook registration explicitly.
- Added regression coverage for `define_errors!`, `#[derive(ModError)]`, coded-error overrides, and feature-gated behavior.

### Changed
- Rewrote the public README and API reference to match the real crate surface, supported attributes, and current recovery model.
- Updated crate-level and recovery module documentation examples to reflect the current API.
- Made `define_errors!` default `fatal` behavior consistent with `ForgeError` and `AppError` by defaulting to `false`.

### Fixed
- Fixed `CodedError::with_retryable(...)` and `CodedError::with_status(...)` so they now apply real per-instance overrides.
- Fixed `define_errors!` helper expansion bugs affecting tag parsing, display formatting, and source chaining.
- Fixed the derive macro so list-style attributes such as `#[error_display("...")]` and `#[error_http_status(...)]` are honored.
- Fixed strict lint failures so `cargo clippy --all-targets --all-features -- -D warnings` now passes cleanly.
- Removed stale documentation that described unsupported APIs or outdated examples.


## [0.9.6] - 2025-08-17

### Added
- Async error handling support via `AsyncForgeError` trait
- Integration with `async-trait` for async error handling in async contexts
- New async utilities like `from_async_result` and `async_handle` methods
- Retry logic with async support in examples
- Comprehensive error recovery module with:
  - Backoff strategies (Exponential, Linear, Fixed) with configurable parameters and jitter
  - Circuit breaker pattern to prevent cascading failures
  - Retry policy framework with custom predicates and backoff support
  - `ForgeErrorRecovery` extension trait for all `ForgeError` types

## [0.9.0] - 2025-08-17

### Added
- Cross-platform CI workflow for testing on Linux, macOS, and Windows
- Clippy checks in CI to ensure code quality
- Windows-specific terminal color detection
- Automatic color disabling for non-interactive terminals
- Thread-safe error hook system using `OnceLock` instead of `static mut`
- Structured context support with `ContextError` type
- Error wrapping with context via `context()` and `with_context()` methods
- Error registry with support for error codes and documentation URLs
- Non-fatal error collection system with `ErrorCollector`
- Optional logging integration with support for:
  - Custom logging implementations
  - Integration with the `log` crate (optional)
  - Integration with the `tracing` crate (optional)
- Improved error chaining with source tracking

### Changed
- Replaced unsafe global mutable state with thread-safe alternatives

### Fixed
- Fixed Clippy warnings:
  - Removed needless doctest main function
  - Fixed collapsible match pattern in derive macros
  - Fixed unused variables and imports
- Addressed deprecated `PanicInfo` usage, replaced with `PanicHookInfo`
- Fixed thread safety test reliability issues
- Fixed `CodedError` state tracking to properly update and report fatal flags
  - Removed collapsible match patterns in error-forge-derive crate
  - Removed unnecessary `fn main()` from doctest examples

## [0.6.3] - 2025-08-17

Historical maintenance release; superseded by `0.9.0`.

## [0.5.0] - 2025 (Prior release)

Initial public release with core functionality.

### Added
- `define_errors!` macro for declarative error definitions
- `ForgeError` trait for unified error handling
- `group!` macro for error composition
- `#[derive(ModError)]` for simplified error implementation
- Console formatting with ANSI color support
- Error hook system with severity levels
- Zero external dependencies design

[Unreleased]: https://github.com/jamesgober/error-forge/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/jamesgober/error-forge/compare/v0.9.8...v1.0.0
[0.9.8]: https://github.com/jamesgober/error-forge/compare/v0.9.7...v0.9.8
[0.9.7]: https://github.com/jamesgober/error-forge/compare/0.9.6...v0.9.7
[0.9.6]: https://github.com/jamesgober/error-forge/compare/0.9.0...v0.9.6
[0.9.0]: https://github.com/jamesgober/error-forge/compare/0.6.3...v0.9.0
[0.6.3]: https://github.com/jamesgober/error-forge/compare/0.6.1...0.6.3