Skip to content

Commit b8f55c3

Browse files
chore(release): anvil-ssh 0.8.0 -> 0.9.0; CHANGELOG (M18 retry/timeout) (#30)
Final Anvil-side slice of M18. Bumps anvil-ssh from 0.8.0 to 0.9.0 to publish the M18.1 + M18.2 work as a single crates.io release. The Gitway-side CLI flags + retry_attempts JSON envelope (M18.4 + M18.5) land against this 0.9.0; the M18.X PRD doc PR closes the milestone with Gitway v1.0.0-rc.9. Cargo.toml: - version "0.8.0" -> "0.9.0" Cargo.lock: - regenerated locally; reflects the 0.9.0 version. CHANGELOG.md: - 0.9.0 entry covering the new anvil_ssh::retry module (RetryPolicy, classify, run, RetryAttempt), the new CAT_RETRY tracing category, the AnvilError::io_kind + is_transient predicates, the three new AnvilConfig fields + builder setters, the apply_ssh_config consumption of ConnectTimeout / ConnectionAttempts, the AnvilSession::connect retry+timeout wrap, and the AnvilSession::retry_history accessor. Documents the proxy/jump scope-narrowing and the HTTP 429/503 out-of-scope decision. Stacked after PRs #28 (M18.1, merged) and #29 (M18.2, merged). Plan: M18.3 of anvil-gitway-milestone-plan.md. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3ef9daa commit b8f55c3

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,37 @@
22

33
All notable changes to Anvil are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [SemVer](https://semver.org/).
44

5+
## [0.9.0] — 2026-05-04
6+
7+
### Added
8+
9+
- **Connection retry, backoff, and timeouts** — the M18 chapter of [Gitway PRD §5.8.7](https://github.com/Steelbore/Gitway/blob/main/Gitway-PRD-v1.0.md), FR-80..FR-83. Closes the M12.6 `ConnectTimeout` / `ConnectionAttempts` deferral loop.
10+
- **New `anvil_ssh::retry` module** — public surface: `RetryPolicy { attempts, base, factor, cap, max_window, connect_timeout }` with builder setters; `Disposition { Retry, Fatal }` + `classify(err)` (FR-82); `RetryAttempt { attempt, reason, elapsed }`; `async fn run<F, Fut, T>(policy, op) -> (T, Vec<RetryAttempt>)` (FR-81, FR-83) — drives a jittered exponential backoff loop with `OsRng` jitter, bails on `max_window` exhaustion, emits a `tracing::warn!` event at the new `CAT_RETRY` category per failed attempt. `run` is timeout-agnostic — the per-attempt `tokio::time::timeout` wrap lives at the call site. Default policy: 3 attempts, 250 ms base, ×2 factor, 8 s cap, 30 s max_window, no connect_timeout.
11+
- **New `anvil_ssh::log::CAT_RETRY = "anvil_ssh::retry"`** appended to `CATEGORIES` for downstream `--debug-categories` validators.
12+
- **`AnvilError::io_kind()`** returns `Option<std::io::ErrorKind>` for the `Io` variant — used by the FR-82 classifier and useful for downstream consumers inspecting failure categories.
13+
- **`AnvilError::is_transient()`** wraps the classifier as a single-call predicate.
14+
- **Three new public `AnvilConfig` fields**`connect_timeout: Option<Duration>`, `connection_attempts: Option<u32>`, `max_retry_window: Option<Duration>` — and matching `AnvilConfigBuilder` setters. Each `None` falls through to `RetryPolicy::default()` at session-build time.
15+
- **`apply_ssh_config` consumes `connect_timeout` + `connection_attempts`** from the parsed `ssh_config` (only when the builder field is `None` — preserves CLI-wins precedence). `max_retry_window` is CLI-only — not in OpenSSH's grammar.
16+
- **`AnvilSession::connect` wrapped in `retry::run`** — each attempt rebuilds `HandlerPieces` (russh consumes the handler), calls `client::connect` inside `tokio::time::timeout` when `connect_timeout` is `Some`, surfaces `Elapsed` as `Io(TimedOut)` so the FR-82 classifier retries it. Auth / host-key / protocol errors are fatal and surface immediately.
17+
- **`AnvilSession::retry_history()`** accessor returns `&[RetryAttempt]` — empty when first attempt succeeded, otherwise the per-attempt history captured during connect. Surfaces in `gitway --test --json`'s `data.retry_attempts` envelope (FR-83).
18+
19+
### Changed
20+
21+
- **`anvil-ssh` minor bump** 0.8.0 → 0.9.0 to signal the new public `retry` module + three new `AnvilConfig` fields + `AnvilSession::retry_history` accessor. Pre-1.0 SemVer: 0.8.x consumers must explicitly opt in.
22+
- **`config.rs::warn_unhonored_directives` removed.** Every `ssh_config(5)` directive Anvil's resolver parses today is now consumed: `HostKeyAlgorithms` / `KexAlgorithms` / `Ciphers` / `MACs` landed in M17; `ConnectTimeout` / `ConnectionAttempts` in M18.
23+
24+
### Notes
25+
26+
- **HTTP 429/503 detection** (FR-82's defensive wording) is out of scope — Anvil speaks raw SSH. HTTP statuses only appear in `ProxyCommand` subprocess output that Anvil doesn't parse; a future ProxyCommand-HTTP-CONNECT milestone may extend `classify` to handle them.
27+
- **Russh-handshake failures are NOT retried.** Once the TCP socket is up, every failure is either a fatal user-input error (auth, host-key) or an in-flight protocol error mid-handshake. `classify` returns `Fatal` for every `russh::Error` variant.
28+
- **Scope-narrowing for the proxy / jump paths.** `connect_via_proxy_command` and `connect_via_jump_hosts` (per-hop + final) construct `AnvilSession` with empty `retry_history` — the ProxyCommand subprocess lifecycle and per-hop `direct-tcpip` channels make retry semantics murkier than the primary path; deferred to a follow-up sub-milestone. `gitway --test` against a direct target host gets the full FR-80..FR-83 coverage today.
29+
- **Public-API additions only** — no breaking changes from 0.8.x.
30+
31+
### Tests
32+
33+
- 15 new unit tests in `retry::tests` covering: default-policy values, builder chainability, classifier matrix (auth-fatal / host-key-fatal / no-key-fatal / io-connection-refused-retry / io-timed-out-retry / io-not-found-retry / io-permission-denied-fatal), run loop (success-first / bail-on-fatal / retry-record-history / exhaust-count), backoff curve (exponential growth, cap enforcement, 1000-draw jitter window).
34+
- Existing 207 lib + integration tests still green; M11–M17 surface unchanged.
35+
536
## [0.8.0] — 2026-05-04
637

738
### Added

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-3.0-or-later
22
[package]
33
name = "anvil-ssh"
4-
version = "0.8.0"
4+
version = "0.9.0"
55
edition = "2021"
66
license = "GPL-3.0-or-later"
77
authors = ["Mohamed Hammad <MJ@S3cure.me>"]

0 commit comments

Comments
 (0)