From 8216db1c712cefdc0e2af7191d501adae03ba392 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Tue, 27 Oct 2026 08:13:38 +1100 Subject: [PATCH] chore(toolchain): bump Rust to 1.98.1 for macOS 27 proc-macro loading (#8598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Bump the pinned Rust toolchain from 1.95.0 to 1.98.1 so release builds work on macOS 27 hosts. Rust ≤ 1.97 strips Apple-target artifacts with its bundled `llvm-objcopy`, which leaves the Mach-O `LC_SYMTAB` string pool (`__LINKEDIT`) only 4-byte aligned. macOS 27's dyld refuses to `dlopen` such images (`mis-aligned LINKEDIT string pool`). Because cargo applies the release profile's default `strip = "debuginfo"` to proc-macro dylibs too, `cargo build --release` of any crate that uses sqlx macros (`buzz-db`, `buzz-relay`, `buzz-admin`, …) fails on macOS 27: rustc cannot load `libsqlx_macros-*.dylib`. Dev builds pass because the dev profile does not strip, which is why this only shows up as a release/packaging failure. The fix (llvm/llvm-project#203680, pulled into rustc by rust-lang/rust#158410 for rust-lang/rust#157750) first shipped in Rust 1.98.0 and was never backported to 1.97.x. `block/buzz-app` pinned 1.98.1 for the same failure in block/buzz-app#529. With this bump the `CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_STRIP=none` / custom-linker workarounds people have been using locally are no longer needed. ### What changes Two commits: the toolchain bump (with the desktop clippy fixes), then the workspace clippy fixes. | Location | Change | |---|---| | `rust-toolchain.toml` | `channel = "1.95.0"` → `"1.98.1"` (profile unchanged) | | `Dockerfile`, `Dockerfile.push-gateway` | `ARG RUST_VERSION=1.95` → `1.98` | | `Dockerfile.sprig` | `rust:1.98-alpine3.22` with the manifest-list digest verified on Docker Hub (shared by the `1.98` and `1.98.1` tags) | | `.github/workflows/release.yml` | `dtolnay/rust-toolchain` pinned to the tip of its `1.98.1` branch (`18f04874`) for the Windows desktop release job | | `benchmarks/harbor-buzz-orchestra` | `RUST_IMAGE = rust:1.98-alpine` | | `_ci-rust.yml`, `windows-canary.yml`, `app_state_tests.rs` | stale "1.95" comments reworded; the `unknown_lints` allow stays with a version-agnostic rationale | | `desktop/src-tauri` | lints clippy 1.98 newly raises under `-D warnings`: `chunks_exact_to_as_chunks` at five fixed-width PCM/UTF-16 decoders, `needless_borrows_for_generic_args` on the `Copy` `norm` closure in `config_bridge/reader.rs`, and a redundant `Engine as _` import in `hpke_key_backup_tests.rs` that `use super::*` already provides | | Workspace crates (9) | the 30 lints clippy 1.98 newly raises in `cargo clippy --workspace --all-targets -- -D warnings`, listed below | Workspace lint fixes: | Lint | Sites | Fix | |---|---|---| | `result_large_err` (20) | `buzz-relay` `api/git/transport.rs` (8), `api/git/settings.rs` (6), `api/gifs.rs`, `api/workflows.rs`, `api/profiles.rs`; `buzz-push-gateway` `verify_installation_assertion` | Clippy 1.98 now checks `async fn`s, so every `async fn … -> Result<_, axum::response::Response>` trips it. These follow the existing allow pattern in `api/media.rs` and `nip_fi_http.rs` ("Response is the natural error type for axum handlers"): module-level in the two git files, per-function elsewhere. Boxing `Response` would touch every handler for no runtime benefit. | | `chunks_exact_to_as_chunks` (3) | `buzz-media` MP4 brand scan; `buzz-voice` npy f32 reader and stereo test fixture | `chunks_exact(N)` → `as_chunks::().0` | | `question_mark` (3) | `buzz-acp` queue flush fallback and slash-command mention skip; `buzz-agent` `find_git_root` | `match … { None => return None }` → `?` | | `useless_format` (2) | `buzz-auth` nip_fi startup test policies | `format!("…")` → `"…".to_string()` | | `useless_borrows_in_formatting` (1) | `buzz-pairing-cli` secret print | drop the borrow | | `manual_noop_waker` (1) | `buzz-sdk` broker test `block_on` | `Waker::noop()` (stable since 1.85) | All `as_chunks` rewrites keep `chunks_exact`'s remainder-dropping semantics, and `as_chunks` is stable since 1.88, which is the MSRV. One change in `api/git/transport.rs` is not a lint fix. Staging that file trips the pre-commit secret scanner on its existing test-only ES256 fixture (`GIT_TEST_EC_PEM`), and the scanner only honours a suppression marker on the flagged line. So the `\`-continued string literal is rewritten as a byte-identical `concat!` whose `BEGIN` line carries `sadscan:disable kingfisher.privkey.2`. Left as is on purpose: `Cargo.toml` `rust-version = "1.88.0"` (MSRV floor, a separate concern), the README/CONTRIBUTING "1.88+" text, Hermit (no Rust packages there), the lockfiles, and the `(1.95.0)` comment in `scripts/start-isolated-test-relay.sh` (the pre-commit secret scanner flags that file's pre-existing dev-only `DATABASE_URL` whenever it is staged). ### Related issue Fixes #8074 ### Testing All under rustc/clippy 1.98.1 on a macOS 27 host (Darwin 27.0.0, aarch64), with no `RUSTFLAGS` / `SDKROOT` / strip overrides in the environment: - `cargo build --release -p buzz-db` (the #8074 repro: the release profile strips the proc-macro dylib): **builds**. The resulting `libsqlx_macros-*.dylib` (14 MB) has `LC_SYMTAB stroff % 8 == 0` per `otool -l`. The same build on 1.95.0 died with `mis-aligned LINKEDIT string pool`. - `just clippy` (workspace, `-D warnings`) and `cargo fmt --check` (workspace and `desktop/src-tauri`): pass - `just desktop-tauri-clippy` (default and `mesh-llm` feature sets): pass - `just test-unit`: every crate passes. The `buzz-acp` binary failed only under pre-push lane contention; rerun solo it is 1060 passed, 0 failed. - `just desktop-tauri-test`: 3422 passed, 0 failed - After the lint fixes: lib/bin unit tests for `buzz-acp`, `buzz-agent`, `buzz-auth`, `buzz-media`, `buzz-voice`, `buzz-sdk`, `buzz-pairing-cli`, and `buzz-relay`'s `api::git::transport` tests, including the Postgres-backed `enforce_mode_git_pack_routes_missing_assertion_exact_bytes`, which parses the rewritten PEM: pass `cfg(windows)` code was not linted locally (no Windows target installed). CI's Windows Rust job runs workspace clippy for `x86_64-pc-windows-msvc`, and that passes on this branch. CI also builds the relay, push-gateway and sprig images on the new `rust:1.98` bases for amd64 and arm64. No runtime behavior changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Matt Toohey Co-authored-by: Claude Fable 5.1 --- .github/workflows/_ci-rust.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/windows-canary.yml | 2 +- Dockerfile | 4 +- Dockerfile.push-gateway | 2 +- Dockerfile.sprig | 2 +- .../scripts/benchmark.py | 2 +- crates/buzz-acp/src/queue.rs | 46 ++++++++----------- crates/buzz-agent/src/hints.rs | 5 +- crates/buzz-auth/src/nip_fi/startup/tests.rs | 4 +- crates/buzz-media/src/validation.rs | 6 ++- crates/buzz-pairing-cli/src/main.rs | 2 +- crates/buzz-push-gateway/src/http.rs | 1 + crates/buzz-relay/src/api/gifs.rs | 2 + crates/buzz-relay/src/api/git/settings.rs | 3 ++ crates/buzz-relay/src/api/git/transport.rs | 15 ++++-- crates/buzz-relay/src/api/profiles.rs | 1 + crates/buzz-relay/src/api/workflows.rs | 2 + crates/buzz-sdk/src/broker/tests.rs | 9 +--- crates/buzz-voice/src/imported.rs | 2 +- crates/buzz-voice/src/pocket_april.rs | 6 ++- desktop/src-tauri/src/app_state_tests.rs | 4 +- .../src-tauri/src/commands/legacy_storage.rs | 8 ++-- .../src-tauri/src/hpke_key_backup_tests.rs | 2 +- desktop/src-tauri/src/huddle/latency_bench.rs | 6 ++- desktop/src-tauri/src/huddle/relay_api.rs | 6 ++- desktop/src-tauri/src/huddle/stt.rs | 6 ++- .../managed_agents/config_bridge/reader.rs | 4 +- rust-toolchain.toml | 2 +- 29 files changed, 85 insertions(+), 73 deletions(-) diff --git a/.github/workflows/_ci-rust.yml b/.github/workflows/_ci-rust.yml index e25639d3ac..4f615eb162 100644 --- a/.github/workflows/_ci-rust.yml +++ b/.github/workflows/_ci-rust.yml @@ -120,7 +120,7 @@ jobs: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # MSVC needs windows.h (aws-lc-sys et al.), so this runs on a real Windows # runner — hermit, used by the Linux jobs, does not provide MSVC. The - # toolchain (1.95.0 + clippy via profile = default) comes from the + # toolchain (1.98.1 + clippy via profile = default) comes from the # repo-root rust-toolchain.toml, which the runner's preinstalled rustup # honors on demand; the host triple already is x86_64-pc-windows-msvc. - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 040817361a..26fbbf5401 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -676,7 +676,7 @@ jobs: shell: bash run: scripts/verify-release-ref.sh desktop-v "$VERSION" - - uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 + - uses: dtolnay/rust-toolchain@18f0487477426dce244608110aa2660c2893deb7 # 1.98.1 with: targets: ${{ env.TARGET }} diff --git a/.github/workflows/windows-canary.yml b/.github/workflows/windows-canary.yml index 7093efd2dc..03114c1a47 100644 --- a/.github/workflows/windows-canary.yml +++ b/.github/workflows/windows-canary.yml @@ -39,7 +39,7 @@ jobs: persist-credentials: false # The Windows runner ships with rustup preinstalled; rust-toolchain.toml - # in the repo root pins the channel (1.95.0) automatically. We only need + # in the repo root pins the channel (1.98.1) automatically. We only need # to ensure the cross-compile target is registered; on windows-latest the # host IS x86_64-pc-windows-msvc so this is typically a no-op. - name: Add Rust target diff --git a/Dockerfile b/Dockerfile index 1f10b0f260..02446e144f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # # Public Buzz relay image — published as ghcr.io/block/buzz:. # -# Builds the `buzz-relay` binary (Rust 1.95) and the `buzz-web` static bundle +# Builds the `buzz-relay` binary (Rust 1.98) and the `buzz-web` static bundle # (pnpm + vite), then assembles them into a small debian-slim runtime with # `git` available (the relay shells out to git for repo hydrate / receive-pack # / upload-pack — see crates/buzz-relay/src/api/git). @@ -11,7 +11,7 @@ # native arm64 runners (see .github/workflows/docker.yml). The Dockerfile # itself is platform-agnostic; do not add --platform pins. -ARG RUST_VERSION=1.95 +ARG RUST_VERSION=1.98 ARG NODE_VERSION=24 ARG DEBIAN_VERSION=bookworm diff --git a/Dockerfile.push-gateway b/Dockerfile.push-gateway index 2b899c89e5..f91e21b58d 100644 --- a/Dockerfile.push-gateway +++ b/Dockerfile.push-gateway @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.7 -ARG RUST_VERSION=1.95 +ARG RUST_VERSION=1.98 ARG DEBIAN_VERSION=bookworm FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS chef diff --git a/Dockerfile.sprig b/Dockerfile.sprig index ee8ac1a64f..a2867f53d0 100644 --- a/Dockerfile.sprig +++ b/Dockerfile.sprig @@ -2,7 +2,7 @@ # Multi-arch is produced by building this file on native amd64 and arm64 runners. # Keep both bases pinned to manifest-list digests so either architecture resolves # to immutable source bytes. -FROM rust:1.95-alpine3.22@sha256:064dfc925d68d1a63f4fd2871bd7dc6e6ea56692989a487185855d62885d90aa AS builder +FROM rust:1.98-alpine3.22@sha256:a1796ca6fa216d6727b5f61c69e4c665b120b1a4dcb969639e2f25f1ed309456 AS builder RUN apk add --no-cache \ build-base \ diff --git a/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py b/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py index 0ad028796c..211730a8d2 100755 --- a/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py +++ b/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py @@ -75,7 +75,7 @@ AGENT_BINARIES = ("buzz-acp", "buzz-agent", "buzz-dev-mcp") FORWARDER_SOURCE = PACKAGE_ROOT / "forwarder" / "relay_forwarder.rs" FORWARDER_BINARY = "relay-forwarder" LINUX_TARGET_DIR = STATE_DIR / "linux-target" -RUST_IMAGE = "rust:1.95-alpine" +RUST_IMAGE = "rust:1.98-alpine" @dataclass(frozen=True) diff --git a/crates/buzz-acp/src/queue.rs b/crates/buzz-acp/src/queue.rs index cc5ae876c1..1cf53baef9 100644 --- a/crates/buzz-acp/src/queue.rs +++ b/crates/buzz-acp/src/queue.rs @@ -558,28 +558,24 @@ impl EventQueue { .keys() .find(|scope| !self.in_flight_scopes.contains(scope)) .cloned(); - match cancelled_scope { - Some(scope) => { - // Move cancelled events into the regular events slot. - // No new events to merge — re-dispatch the original batch. - let cancelled = self.cancelled_batches.remove(&scope).unwrap_or_default(); - let cancel_reason = self.cancel_reasons.remove(&scope); - self.in_flight_scopes.insert(scope.clone()); - self.in_flight_deadlines - .insert(scope.clone(), now + self.in_flight_deadline); - self.in_flight_batch_sizes - .insert(scope.clone(), cancelled.len()); - self.record_in_flight_reply_route(&scope, &cancelled); - return Some(FlushBatch { - channel_id: scope.channel_id(), - scope, - events: cancelled, - cancelled_events: vec![], - cancel_reason, - }); - } - None => return None, - } + let scope = cancelled_scope?; + // Move cancelled events into the regular events slot. + // No new events to merge — re-dispatch the original batch. + let cancelled = self.cancelled_batches.remove(&scope).unwrap_or_default(); + let cancel_reason = self.cancel_reasons.remove(&scope); + self.in_flight_scopes.insert(scope.clone()); + self.in_flight_deadlines + .insert(scope.clone(), now + self.in_flight_deadline); + self.in_flight_batch_sizes + .insert(scope.clone(), cancelled.len()); + self.record_in_flight_reply_route(&scope, &cancelled); + return Some(FlushBatch { + channel_id: scope.channel_id(), + scope, + events: cancelled, + cancelled_events: vec![], + cancel_reason, + }); } }; let channel_id = scope.channel_id(); @@ -1465,10 +1461,8 @@ pub fn extract_slash_command(content: &str, known_names: &[&str]) -> Option 0).then_some(len) }); - match name_len { - Some(len) => rest = after_at[len..].trim_start(), - None => return None, // bare '@' — not a mention - } + let len = name_len?; // bare '@' — not a mention + rest = after_at[len..].trim_start(); } else { break; } diff --git a/crates/buzz-agent/src/hints.rs b/crates/buzz-agent/src/hints.rs index 9fb99f0ebc..22c4b4fcac 100644 --- a/crates/buzz-agent/src/hints.rs +++ b/crates/buzz-agent/src/hints.rs @@ -30,10 +30,7 @@ fn find_git_root(start: &Path) -> Option { if current.join(".git").exists() { return Some(current); } - match current.parent() { - Some(parent) => current = parent.to_path_buf(), - None => return None, - } + current = current.parent()?.to_path_buf(); } } diff --git a/crates/buzz-auth/src/nip_fi/startup/tests.rs b/crates/buzz-auth/src/nip_fi/startup/tests.rs index a328c8e21e..123289ee18 100644 --- a/crates/buzz-auth/src/nip_fi/startup/tests.rs +++ b/crates/buzz-auth/src/nip_fi/startup/tests.rs @@ -17,7 +17,7 @@ fn test_contract(issuer: &str) -> JwksSourceContract { fn make_offline_policy(issuer: &str) -> IssuerPolicy { IssuerPolicy::new( issuer.to_owned(), - vec![format!("https://relay.example/api")], + vec!["https://relay.example/api".to_string()], TokenClass::DedicatedNipFi, FreshnessClass::OfflineJwt, vec![JwtAlgorithm::ES256], @@ -32,7 +32,7 @@ fn make_offline_policy(issuer: &str) -> IssuerPolicy { fn make_status_policy(issuer: &str) -> IssuerPolicy { IssuerPolicy::new( issuer.to_owned(), - vec![format!("https://relay.example/api")], + vec!["https://relay.example/api".to_string()], TokenClass::DedicatedNipFi, FreshnessClass::CurrentStatus, vec![JwtAlgorithm::ES256], diff --git a/crates/buzz-media/src/validation.rs b/crates/buzz-media/src/validation.rs index 980e28d8cb..0a748ca852 100644 --- a/crates/buzz-media/src/validation.rs +++ b/crates/buzz-media/src/validation.rs @@ -56,8 +56,10 @@ pub(crate) fn looks_like_mp4_iso_bmff(bytes: &[u8]) -> bool { let major = payload[..4].try_into().ok(); major.is_some_and(|brand| MP4_BRANDS.contains(&brand)) || payload[8..] - .chunks_exact(4) - .any(|brand| MP4_BRANDS.iter().any(|candidate| brand == candidate)) + .as_chunks::<4>() + .0 + .iter() + .any(|brand| MP4_BRANDS.contains(brand)) } /// MIME types blocked from the generic file-upload path. diff --git a/crates/buzz-pairing-cli/src/main.rs b/crates/buzz-pairing-cli/src/main.rs index ca14ecebe9..2d03d594a8 100644 --- a/crates/buzz-pairing-cli/src/main.rs +++ b/crates/buzz-pairing-cli/src/main.rs @@ -327,7 +327,7 @@ async fn cmd_target(relay_override: Option, show_secret: bool) -> Result }; println!("Received {kind_label} payload!"); if show_secret { - println!("{kind_label}: {}", &*payload); + println!("{kind_label}: {}", *payload); } else { println!("(use --show-secret to display the received secret)"); } diff --git a/crates/buzz-push-gateway/src/http.rs b/crates/buzz-push-gateway/src/http.rs index ca1e18936c..f0fbfde5fe 100644 --- a/crates/buzz-push-gateway/src/http.rs +++ b/crates/buzz-push-gateway/src/http.rs @@ -311,6 +311,7 @@ struct AssertionChallenge<'a> { text: &'a str, } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn verify_installation_assertion( s: &AppState, installation_id: uuid::Uuid, diff --git a/crates/buzz-relay/src/api/gifs.rs b/crates/buzz-relay/src/api/gifs.rs index 8dd8bdf768..c1d087dfce 100644 --- a/crates/buzz-relay/src/api/gifs.rs +++ b/crates/buzz-relay/src/api/gifs.rs @@ -223,6 +223,7 @@ pub async fn search( search_inner(state, headers, body).await.into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn search_inner( state: Arc, headers: HeaderMap, @@ -299,6 +300,7 @@ pub async fn share( share_inner(state, headers, body).await.into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn share_inner( state: Arc, headers: HeaderMap, diff --git a/crates/buzz-relay/src/api/git/settings.rs b/crates/buzz-relay/src/api/git/settings.rs index 00e70998a1..f11e11026b 100644 --- a/crates/buzz-relay/src/api/git/settings.rs +++ b/crates/buzz-relay/src/api/git/settings.rs @@ -5,6 +5,9 @@ //! strict NIP-98 request prevents reusable Smart HTTP credentials authorizing //! metadata changes (URL, method, payload and replay are all checked). +// Response is the natural error type for axum handlers. +#![allow(clippy::result_large_err)] + use std::sync::Arc; use axum::{ diff --git a/crates/buzz-relay/src/api/git/transport.rs b/crates/buzz-relay/src/api/git/transport.rs index 72b34bbe77..1c0c08a600 100644 --- a/crates/buzz-relay/src/api/git/transport.rs +++ b/crates/buzz-relay/src/api/git/transport.rs @@ -8,6 +8,9 @@ //! Auth: NIP-98 on all routes (clone + push). No public repos for v1. //! Transport: shells out to `git --stateless-rpc` with `env_clear()`. +// Response is the natural error type for axum handlers. +#![allow(clippy::result_large_err)] + use std::future::Future; use std::path::Path; use std::sync::Arc; @@ -4827,11 +4830,13 @@ mod off_mode_precedence_tests { const GIT_TEST_ISSUER: &str = "https://git-pack-test.issuer.invalid"; const GIT_TEST_AUDIENCE: &str = "https://git-pack-test.relay.invalid"; const GIT_TEST_KID: &str = "git-pack-test-key-1"; - const GIT_TEST_EC_PEM: &str = "-----BEGIN PRIVATE KEY-----\n\ - MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcnxDM4EiirH9dHUE\n\ - WZc759TX4s5PAn8kO5ovXSnGxCWhRANCAARFb6ZnsfkqOOXyEhj3KBQphGKF4vTa\n\ - zhebbavbZ1ZoklqkF1cGg+jTO7rONAVEzXvXUWtV6CdDV+rybiVmFP2w\n\ - -----END PRIVATE KEY-----\n"; + const GIT_TEST_EC_PEM: &str = concat!( + "-----BEGIN PRIVATE KEY-----\n", // sadscan:disable kingfisher.privkey.2 -- test-only ES256 fixture + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcnxDM4EiirH9dHUE\n", + "WZc759TX4s5PAn8kO5ovXSnGxCWhRANCAARFb6ZnsfkqOOXyEhj3KBQphGKF4vTa\n", + "zhebbavbZ1ZoklqkF1cGg+jTO7rONAVEzXvXUWtV6CdDV+rybiVmFP2w\n", + "-----END PRIVATE KEY-----\n", + ); let jwks: JwkSet = serde_json::from_value(serde_json::json!({ "keys": [{ diff --git a/crates/buzz-relay/src/api/profiles.rs b/crates/buzz-relay/src/api/profiles.rs index a1c467a445..66885cf283 100644 --- a/crates/buzz-relay/src/api/profiles.rs +++ b/crates/buzz-relay/src/api/profiles.rs @@ -82,6 +82,7 @@ pub async fn search( search_inner(state, headers, body).await.into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn search_inner( state: Arc, headers: HeaderMap, diff --git a/crates/buzz-relay/src/api/workflows.rs b/crates/buzz-relay/src/api/workflows.rs index bd33493191..957a238a80 100644 --- a/crates/buzz-relay/src/api/workflows.rs +++ b/crates/buzz-relay/src/api/workflows.rs @@ -158,6 +158,7 @@ pub async fn workflow_runs( .into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn workflow_runs_inner( state: Arc, workflow_id: Uuid, @@ -235,6 +236,7 @@ pub async fn run_approvals( .into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn run_approvals_inner( state: Arc, workflow_id: Uuid, diff --git a/crates/buzz-sdk/src/broker/tests.rs b/crates/buzz-sdk/src/broker/tests.rs index 14a55005ae..57031089a6 100644 --- a/crates/buzz-sdk/src/broker/tests.rs +++ b/crates/buzz-sdk/src/broker/tests.rs @@ -2602,13 +2602,8 @@ impl BrokerClient for DoubleBroker { fn block_on(future: F) -> F::Output { // A hand-rolled park-free executor: the double's future is always ready, so // one poll suffices and pulling in a runtime would be the heavier choice. - use std::task::{Context, Poll, Wake, Waker}; - struct NoopWake; - impl Wake for NoopWake { - fn wake(self: std::sync::Arc) {} - } - let waker = Waker::from(std::sync::Arc::new(NoopWake)); - let mut context = Context::from_waker(&waker); + use std::task::{Context, Poll, Waker}; + let mut context = Context::from_waker(Waker::noop()); let mut future = Box::pin(future); match future.as_mut().poll(&mut context) { Poll::Ready(output) => output, diff --git a/crates/buzz-voice/src/imported.rs b/crates/buzz-voice/src/imported.rs index 6f0ea71cad..5dbe9297f8 100644 --- a/crates/buzz-voice/src/imported.rs +++ b/crates/buzz-voice/src/imported.rs @@ -580,7 +580,7 @@ mod tests { let mono = fixture(sample_rate, seconds, amplitude); let mono_data = &mono[44..]; let mut stereo_data = Vec::with_capacity(mono_data.len() * 2); - for sample in mono_data.chunks_exact(2) { + for sample in mono_data.as_chunks::<2>().0 { stereo_data.extend_from_slice(sample); stereo_data.extend_from_slice(sample); } diff --git a/crates/buzz-voice/src/pocket_april.rs b/crates/buzz-voice/src/pocket_april.rs index 9ace5001da..43cbbd77f8 100644 --- a/crates/buzz-voice/src/pocket_april.rs +++ b/crates/buzz-voice/src/pocket_april.rs @@ -1336,8 +1336,10 @@ fn read_npy_f32(path: &Path) -> Result, String> { return Err(format!("misaligned float32 data in {}", path.display())); } Ok(data - .chunks_exact(4) - .map(|chunk| f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|chunk| f32::from_le_bytes(*chunk)) .collect()) } diff --git a/desktop/src-tauri/src/app_state_tests.rs b/desktop/src-tauri/src/app_state_tests.rs index ceef4d3f93..8e314e75ce 100644 --- a/desktop/src-tauri/src/app_state_tests.rs +++ b/desktop/src-tauri/src/app_state_tests.rs @@ -706,8 +706,8 @@ fn present_keyring_with_mismatched_file_adopts_file_key_marker_failure_keeps_fil // so removing write permission on the dir blocks the write entirely. let dir_perms_orig = std::fs::metadata(dir.path()).unwrap().permissions(); let mut dir_perms_ro = dir_perms_orig.clone(); - // unknown_lints: the clippy lint below doesn't exist yet in the pinned - // 1.95 toolchain but does in CI's newer clippy — allow both worlds. + // unknown_lints: keep this compiling on clippy versions that predate the + // lint below, so the pinned toolchain and any newer clippy both accept it. #[allow(unknown_lints)] #[allow(clippy::permissions_set_readonly_value)] dir_perms_ro.set_readonly(true); diff --git a/desktop/src-tauri/src/commands/legacy_storage.rs b/desktop/src-tauri/src/commands/legacy_storage.rs index fa7854d023..c140e342da 100644 --- a/desktop/src-tauri/src/commands/legacy_storage.rs +++ b/desktop/src-tauri/src/commands/legacy_storage.rs @@ -81,11 +81,13 @@ fn decode_webkit_local_storage_value(bytes: &[u8]) -> Option { } if bytes.len().is_multiple_of(2) { - let has_utf16_ascii_shape = bytes.chunks_exact(2).any(|chunk| chunk[1] == 0); + let has_utf16_ascii_shape = bytes.as_chunks::<2>().0.iter().any(|chunk| chunk[1] == 0); if has_utf16_ascii_shape { let utf16: Vec = bytes - .chunks_exact(2) - .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|chunk| u16::from_le_bytes(*chunk)) .collect(); if let Ok(value) = String::from_utf16(&utf16) { return Some(value.trim_end_matches('\0').to_string()); diff --git a/desktop/src-tauri/src/hpke_key_backup_tests.rs b/desktop/src-tauri/src/hpke_key_backup_tests.rs index 3b15964ba5..5bfa7a90bc 100644 --- a/desktop/src-tauri/src/hpke_key_backup_tests.rs +++ b/desktop/src-tauri/src/hpke_key_backup_tests.rs @@ -1,4 +1,4 @@ -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use base64::engine::general_purpose::URL_SAFE_NO_PAD; use nostr::{Keys, SecretKey, ToBech32}; use rustls::crypto::hpke::{EncapsulatedSecret, Hpke, HpkePrivateKey}; diff --git a/desktop/src-tauri/src/huddle/latency_bench.rs b/desktop/src-tauri/src/huddle/latency_bench.rs index f928ddbce0..04ae66554d 100644 --- a/desktop/src-tauri/src/huddle/latency_bench.rs +++ b/desktop/src-tauri/src/huddle/latency_bench.rs @@ -64,8 +64,10 @@ fn read_wav_f32_48k(path: &str) -> Vec { assert!(fmt_ok, "fmt chunk missing"); let (off, len) = data.expect("data chunk missing"); bytes[off..off + len] - .chunks_exact(4) - .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|b| f32::from_le_bytes(*b)) .collect() } diff --git a/desktop/src-tauri/src/huddle/relay_api.rs b/desktop/src-tauri/src/huddle/relay_api.rs index 67f205c2db..8495332978 100644 --- a/desktop/src-tauri/src/huddle/relay_api.rs +++ b/desktop/src-tauri/src/huddle/relay_api.rs @@ -400,8 +400,10 @@ async fn audio_relay_pipeline(args: AudioRelayPipelineArgs) -> Result<(), String continue; // Malformed batch. } let samples: Vec = pcm_bytes - .chunks_exact(4) - .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|b| f32::from_le_bytes(*b)) .collect(); let mut tx = ws_tx_send.lock().await; diff --git a/desktop/src-tauri/src/huddle/stt.rs b/desktop/src-tauri/src/huddle/stt.rs index c27bf38b64..897844e103 100644 --- a/desktop/src-tauri/src/huddle/stt.rs +++ b/desktop/src-tauri/src/huddle/stt.rs @@ -832,8 +832,10 @@ fn vad_flush_allowed(ptt_mode: bool, manually_open: bool, ptt_held: bool) -> boo /// byte order, which is LE on all supported platforms. fn bytes_to_f32(bytes: &[u8]) -> Vec { bytes - .chunks_exact(4) - .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|b| f32::from_le_bytes(*b)) .collect() } diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs b/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs index 84eec8db33..0382d4d572 100644 --- a/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs +++ b/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs @@ -607,7 +607,7 @@ fn build_thinking_field( let rec_native = thinking_env_var .and_then(|k| super::effort::get_ci(&record.env_vars, k)) .and_then(|v| norm(v)); - let column = record.effort_level.as_deref().and_then(&norm); + let column = record.effort_level.as_deref().and_then(norm); let rec_legacy = thinking_env_var .filter(|k| *k != LEGACY_THINKING_EFFORT_KEY) .and_then(|_| super::effort::get_ci(&record.env_vars, LEGACY_THINKING_EFFORT_KEY)) @@ -620,7 +620,7 @@ fn build_thinking_field( let glob = thinking_env_var.and_then(|k| effort_tier_alias(&tiers.global_env, k, norm, false)); let def = thinking_env_var.and_then(|k| effort_tier_alias(&tiers.definition_env, k, norm, false)); - let file = file_effort.as_deref().and_then(&norm); + let file = file_effort.as_deref().and_then(norm); // Live ACP value: normalized through the runtime CONTRACT only, never the // persisted `effort_accepted` vocabulary. The ACP running value comes from diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f92020c653..99e619bb8c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.95.0" +channel = "1.98.1" profile = "default"