diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 648307bd0..fdcb1f567 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,6 @@ concurrency: # to anonymous, which is what the cache is there to make rare. env: HF_TOKEN: ${{ secrets.HF_TOKEN }} - RUSTUP_TOOLCHAIN: stable jobs: fmt: @@ -33,8 +32,6 @@ jobs: clippy: runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v4 @@ -83,8 +80,6 @@ jobs: test: runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v4 @@ -133,7 +128,6 @@ jobs: docs: runs-on: ubuntu-latest env: - RUSTUP_TOOLCHAIN: stable RUSTDOCFLAGS: -D warnings steps: - uses: actions/checkout@v4 @@ -162,8 +156,6 @@ jobs: check-workshop: runs-on: windows-latest - env: - RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v4 @@ -234,8 +226,6 @@ jobs: # missing-system-package class of failure the Windows-only job cannot see. check-workshop-linux: runs-on: ubuntu-22.04 - env: - RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v4 @@ -341,35 +331,6 @@ jobs: working-directory: crates/gateway-config-ui/ui run: npm test - msrv: - runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: 1.89.0 - steps: - - uses: actions/checkout@v4 - - - uses: dtolnay/rust-toolchain@1.89.0 - - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - - - name: Cache the embedding model - uses: ./.github/actions/hf-model-cache - - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - cache-dependency-path: crates/*/ui/package-lock.json - - - name: Install UI dependencies - run: | - npm ci --prefix crates/workshop-server/ui - npm ci --prefix crates/gateway-config-ui/ui - - - name: Check on MSRV - run: cargo check --locked --workspace --exclude workshop --exclude workshop-server --all-targets --all-features - supply-chain: runs-on: ubuntu-latest steps: @@ -391,7 +352,7 @@ jobs: # or that job bypasses this gate. ci-green: runs-on: ubuntu-latest - needs: [fmt, clippy, test, docs, check-workshop, check-workshop-linux, ui, msrv, supply-chain] + needs: [fmt, clippy, test, docs, check-workshop, check-workshop-linux, ui, supply-chain] if: always() steps: - name: Verify every job succeeded diff --git a/.github/workflows/release-workshop.yml b/.github/workflows/release-workshop.yml index b05f74616..c392a6849 100644 --- a/.github/workflows/release-workshop.yml +++ b/.github/workflows/release-workshop.yml @@ -26,11 +26,6 @@ concurrency: group: release-workshop-${{ github.ref_name }} cancel-in-progress: false -env: - # The repo's rust-toolchain.toml pins the MSRV for local builds; CI tests - # the current stable. - RUSTUP_TOOLCHAIN: stable - jobs: prepare: runs-on: ubuntu-latest diff --git a/.github/workflows/stt-miri.yml b/.github/workflows/stt-miri.yml index 0ffabd4df..fb402c775 100644 --- a/.github/workflows/stt-miri.yml +++ b/.github/workflows/stt-miri.yml @@ -46,16 +46,9 @@ jobs: native-whisper: runs-on: [self-hosted, windows, cuda] timeout-minutes: 90 - env: - RUSTUP_TOOLCHAIN: 1.89 - RUSTUP_AUTO_INSTALL: "0" steps: - uses: actions/checkout@v4 - - name: Verify preinstalled MSRV Rust - shell: powershell - run: tools/validate-rust-1.89.0.ps1 - - name: Cache Cargo uses: Swatinem/rust-cache@v2 diff --git a/AGENTS.md b/AGENTS.md index 18f83ea38..9c5721f2e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,4 +12,3 @@ Multi-crate Rust workspace for the PromptForge pipeline runtime, inference gatew - Long-running work reports through `shared-progress`. Producers report operation state, hosts forward it, and renderers format it. - Unsafe code stays in its explicitly owned boundary. Every unsafe block documents its safety invariants immediately before the block. - Comments explain a non-obvious constraint, ordering requirement, or workaround. Every platform or external-bug workaround cites its upstream issue URL in the explanatory comment. -- CI lints with a newer stable toolchain than the pinned local MSRV (`rust-toolchain.toml`). Lints introduced or strengthened after the pin, such as unused glob imports, may fail only on CI. Check with the newer toolchain before pushing when a change touches imports or doc comments; `cargo doc` runs with warnings denied on CI. diff --git a/Cargo.toml b/Cargo.toml index 9892cf57c..4b8d86fab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ default-members = ["crates/gateway"] [workspace.package] version = "0.3.0" edition = "2024" -rust-version = "1.89" license = "BSL-1.0" repository = "https://github.com/cppalliance/promptforge" diff --git a/README.md b/README.md index 7ec41fa83..2a3232d54 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![CI](https://github.com/cppalliance/promptforge/actions/workflows/ci.yml/badge.svg)](https://github.com/cppalliance/promptforge/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/license-BSL--1.0-blue.svg)](LICENSE) -[![Rust](https://img.shields.io/badge/rust-1.89%2B-orange.svg)](https://www.rust-lang.org/) # PromptForge @@ -65,7 +64,7 @@ Lua sets up the turn. The prose before a Lua block is that block's lazy `prose` ## Build from source -Every build needs Rust 1.89 or later and Node.js 22. The two web UIs are bundled with esbuild during the Cargo build, so run `npm ci` once in each `ui/` folder after cloning: +Every build needs Rust and Node.js 22. The two web UIs are bundled with esbuild during the Cargo build, so run `npm ci` once in each `ui/` folder after cloning: ```bash git clone git@github.com:cppalliance/promptforge.git @@ -109,10 +108,6 @@ Build the guide locally with `mdbook build guide`. ![Filing cabinets](images/banner-06.png) -## Minimum Rust Version - -Rust 1.89 or later. - ## Contributing Build, format, and test before you open a PR. CI runs `cargo fmt --check`, `clippy -D warnings`, and `cargo test --workspace`. diff --git a/crates/build-llama-cuda/Cargo.toml b/crates/build-llama-cuda/Cargo.toml index 97b492a43..1e38afa57 100644 --- a/crates/build-llama-cuda/Cargo.toml +++ b/crates/build-llama-cuda/Cargo.toml @@ -2,7 +2,6 @@ name = "build-llama-cuda" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/build-ui/Cargo.toml b/crates/build-ui/Cargo.toml index 630dc1448..db477f4e2 100644 --- a/crates/build-ui/Cargo.toml +++ b/crates/build-ui/Cargo.toml @@ -2,7 +2,6 @@ name = "build-ui" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/build-workshop/Cargo.toml b/crates/build-workshop/Cargo.toml index 94154afc6..e4955242f 100644 --- a/crates/build-workshop/Cargo.toml +++ b/crates/build-workshop/Cargo.toml @@ -2,7 +2,6 @@ name = "build-workshop" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-config-ui/Cargo.toml b/crates/gateway-config-ui/Cargo.toml index 18af9f1f7..864a9733f 100644 --- a/crates/gateway-config-ui/Cargo.toml +++ b/crates/gateway-config-ui/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-config-ui" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-config/Cargo.toml b/crates/gateway-config/Cargo.toml index 2134acd42..1bbfa27f7 100644 --- a/crates/gateway-config/Cargo.toml +++ b/crates/gateway-config/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-config" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-local/Cargo.toml b/crates/gateway-local/Cargo.toml index 83464c56e..faecf06d5 100644 --- a/crates/gateway-local/Cargo.toml +++ b/crates/gateway-local/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-local" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-local/src/artifacts.rs b/crates/gateway-local/src/artifacts.rs index 2ea6d9512..a8e55655e 100644 --- a/crates/gateway-local/src/artifacts.rs +++ b/crates/gateway-local/src/artifacts.rs @@ -77,7 +77,7 @@ const DOWNLOAD_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_ /// accepts the connection and then sends nothing can never pin the /// provisioning thread forever - and a reader thread parked past the idle /// bound reaps when the ceiling drops its body. -const DOWNLOAD_REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(2 * 60 * 60); +const DOWNLOAD_REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_hours(2); type Result = std::result::Result; diff --git a/crates/gateway-local/src/chat_templates/live.rs b/crates/gateway-local/src/chat_templates/live.rs index 334eec61e..ffb0e85ad 100644 --- a/crates/gateway-local/src/chat_templates/live.rs +++ b/crates/gateway-local/src/chat_templates/live.rs @@ -13,7 +13,7 @@ const LIVE_ENV: &str = "PROMPTFORGE_LIVE_CHAT_TEMPLATES"; const SERVER_ENV: &str = "PROMPTFORGE_LLAMA_SERVER"; const MODELS_ENV: &str = "PROMPTFORGE_CHAT_TEMPLATE_MODELS"; const API_KEY: &str = "promptforge-chat-template-live"; -const START_TIMEOUT: Duration = Duration::from_secs(20 * 60); +const START_TIMEOUT: Duration = Duration::from_mins(20); static LIVE_SERIAL: Mutex<()> = Mutex::new(()); #[derive(Debug, Deserialize)] diff --git a/crates/gateway-logging/Cargo.toml b/crates/gateway-logging/Cargo.toml index ed3095493..c7c958845 100644 --- a/crates/gateway-logging/Cargo.toml +++ b/crates/gateway-logging/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-logging" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-routing/Cargo.toml b/crates/gateway-routing/Cargo.toml index 0042a07c6..907c2be33 100644 --- a/crates/gateway-routing/Cargo.toml +++ b/crates/gateway-routing/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-routing" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-stt-backend-whisper/Cargo.toml b/crates/gateway-stt-backend-whisper/Cargo.toml index 2a02d34ab..890cf93c9 100644 --- a/crates/gateway-stt-backend-whisper/Cargo.toml +++ b/crates/gateway-stt-backend-whisper/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-stt-backend-whisper" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-stt-engine/Cargo.toml b/crates/gateway-stt-engine/Cargo.toml index cefee55a5..724e6445c 100644 --- a/crates/gateway-stt-engine/Cargo.toml +++ b/crates/gateway-stt-engine/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-stt-engine" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-stt-engine/src/test_fixtures/tests/scenario_cleanup/decode.rs b/crates/gateway-stt-engine/src/test_fixtures/tests/scenario_cleanup/decode.rs index 5fe224321..637d4f094 100644 --- a/crates/gateway-stt-engine/src/test_fixtures/tests/scenario_cleanup/decode.rs +++ b/crates/gateway-stt-engine/src/test_fixtures/tests/scenario_cleanup/decode.rs @@ -71,7 +71,7 @@ async fn canceled_decode_scenario_releases_and_permits_a_follow_up() { engine.shutdown().expect("worker joins"); } -#[tokio::test(start_paused = true)] +#[tokio::test] async fn decode_rendezvous_timeout_releases_a_late_arrival_and_permits_a_follow_up() { let decoder = ScriptedDecoder::new(); decoder.push_text("late arrival"); diff --git a/crates/gateway-stt/Cargo.toml b/crates/gateway-stt/Cargo.toml index ff3c3ed37..d2d0b5c41 100644 --- a/crates/gateway-stt/Cargo.toml +++ b/crates/gateway-stt/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-stt" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-stt/src/audio.rs b/crates/gateway-stt/src/audio.rs index ffc97a925..5631cf896 100644 --- a/crates/gateway-stt/src/audio.rs +++ b/crates/gateway-stt/src/audio.rs @@ -314,7 +314,7 @@ mod tests { assert!( error .source() - .is_some_and(<(dyn std::error::Error + 'static)>::is::), + .is_some_and(::is::), "the error chain carries the decoder failure" ); } diff --git a/crates/gateway-web-search/Cargo.toml b/crates/gateway-web-search/Cargo.toml index 0061abc00..abbfe8455 100644 --- a/crates/gateway-web-search/Cargo.toml +++ b/crates/gateway-web-search/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-web-search" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway-whisper-ffi/Cargo.toml b/crates/gateway-whisper-ffi/Cargo.toml index 144e9f7f0..e5195beaa 100644 --- a/crates/gateway-whisper-ffi/Cargo.toml +++ b/crates/gateway-whisper-ffi/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway-whisper-ffi" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway/Cargo.toml b/crates/gateway/Cargo.toml index f33fc5894..f383e2dac 100644 --- a/crates/gateway/Cargo.toml +++ b/crates/gateway/Cargo.toml @@ -2,7 +2,6 @@ name = "gateway" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/gateway/tests/it/cuda.rs b/crates/gateway/tests/it/cuda.rs index 538875d0b..84760afbd 100644 --- a/crates/gateway/tests/it/cuda.rs +++ b/crates/gateway/tests/it/cuda.rs @@ -36,12 +36,12 @@ const PROJECTOR_URL: &str = const PROJECTOR_SHA256: &str = "140be8d7849741f88c50757d529b84373ee8e27052cc2236855b537f4a8215fa"; /// First provisioning downloads the three pinned artifacts. -const PROVISION_TIMEOUT: Duration = Duration::from_secs(45 * 60); +const PROVISION_TIMEOUT: Duration = Duration::from_mins(45); /// A marker-hit relaunch skips downloads and re-hashing; only spawn and /// weight load remain. -const RELAUNCH_TIMEOUT: Duration = Duration::from_secs(15 * 60); +const RELAUNCH_TIMEOUT: Duration = Duration::from_mins(15); /// One completion against a warm server. -const COMPLETION_TIMEOUT: Duration = Duration::from_secs(5 * 60); +const COMPLETION_TIMEOUT: Duration = Duration::from_mins(5); /// Bound on waiting for the capture readers to drain the child's startup /// log: readiness is an HTTP probe, so it can beat the final piped bytes. const DIAGNOSTICS_TIMEOUT: Duration = Duration::from_secs(30); diff --git a/crates/product-integration-tests/Cargo.toml b/crates/product-integration-tests/Cargo.toml index e27c7bdf7..25ebe4c24 100644 --- a/crates/product-integration-tests/Cargo.toml +++ b/crates/product-integration-tests/Cargo.toml @@ -2,7 +2,6 @@ name = "product-integration-tests" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-agent/Cargo.toml b/crates/promptforge-agent/Cargo.toml index b8dfa9612..791181a78 100644 --- a/crates/promptforge-agent/Cargo.toml +++ b/crates/promptforge-agent/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-agent" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-core-support/Cargo.toml b/crates/promptforge-core-support/Cargo.toml index b9b878ddc..7b72dc5e0 100644 --- a/crates/promptforge-core-support/Cargo.toml +++ b/crates/promptforge-core-support/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-core-support" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-core/Cargo.toml b/crates/promptforge-core/Cargo.toml index bd4926076..c1023e474 100644 --- a/crates/promptforge-core/Cargo.toml +++ b/crates/promptforge-core/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-core" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-lua/Cargo.toml b/crates/promptforge-lua/Cargo.toml index d825bac18..06fce1ddf 100644 --- a/crates/promptforge-lua/Cargo.toml +++ b/crates/promptforge-lua/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-lua" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-model-client/Cargo.toml b/crates/promptforge-model-client/Cargo.toml index 49ac259b4..dd7e3ff1a 100644 --- a/crates/promptforge-model-client/Cargo.toml +++ b/crates/promptforge-model-client/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-model-client" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-parser/Cargo.toml b/crates/promptforge-parser/Cargo.toml index 01c7cefb4..4d9338de3 100644 --- a/crates/promptforge-parser/Cargo.toml +++ b/crates/promptforge-parser/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-parser" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-store/Cargo.toml b/crates/promptforge-store/Cargo.toml index fe3cd3d23..7b4112c89 100644 --- a/crates/promptforge-store/Cargo.toml +++ b/crates/promptforge-store/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-store" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-tool-picker/Cargo.toml b/crates/promptforge-tool-picker/Cargo.toml index 37da56cc8..55e735021 100644 --- a/crates/promptforge-tool-picker/Cargo.toml +++ b/crates/promptforge-tool-picker/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-tool-picker" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-tools/Cargo.toml b/crates/promptforge-tools/Cargo.toml index 7c7799da7..5f5e21589 100644 --- a/crates/promptforge-tools/Cargo.toml +++ b/crates/promptforge-tools/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-tools" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-web-search/Cargo.toml b/crates/promptforge-web-search/Cargo.toml index 81bad464a..e264dd049 100644 --- a/crates/promptforge-web-search/Cargo.toml +++ b/crates/promptforge-web-search/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-web-search" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge-webfetch/Cargo.toml b/crates/promptforge-webfetch/Cargo.toml index ea5f8d298..439267ae7 100644 --- a/crates/promptforge-webfetch/Cargo.toml +++ b/crates/promptforge-webfetch/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge-webfetch" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/promptforge/Cargo.toml b/crates/promptforge/Cargo.toml index a30bd618a..1cf133608 100644 --- a/crates/promptforge/Cargo.toml +++ b/crates/promptforge/Cargo.toml @@ -2,7 +2,6 @@ name = "promptforge" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/shared-loopback/Cargo.toml b/crates/shared-loopback/Cargo.toml index baefe44d4..0d90f25e2 100644 --- a/crates/shared-loopback/Cargo.toml +++ b/crates/shared-loopback/Cargo.toml @@ -2,7 +2,6 @@ name = "shared-loopback" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/shared-progress/Cargo.toml b/crates/shared-progress/Cargo.toml index d061dc596..470719c94 100644 --- a/crates/shared-progress/Cargo.toml +++ b/crates/shared-progress/Cargo.toml @@ -2,7 +2,6 @@ name = "shared-progress" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/shared-protocol/Cargo.toml b/crates/shared-protocol/Cargo.toml index 1987be118..83582ebc4 100644 --- a/crates/shared-protocol/Cargo.toml +++ b/crates/shared-protocol/Cargo.toml @@ -2,7 +2,6 @@ name = "shared-protocol" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/shared-sidecar/Cargo.toml b/crates/shared-sidecar/Cargo.toml index 91912cb07..97f9f1c9c 100644 --- a/crates/shared-sidecar/Cargo.toml +++ b/crates/shared-sidecar/Cargo.toml @@ -2,7 +2,6 @@ name = "shared-sidecar" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/workshop-server/Cargo.toml b/crates/workshop-server/Cargo.toml index 248361982..3fa5c5366 100644 --- a/crates/workshop-server/Cargo.toml +++ b/crates/workshop-server/Cargo.toml @@ -2,7 +2,6 @@ name = "workshop-server" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/workshop-server/src/backoff.rs b/crates/workshop-server/src/backoff.rs index 571838647..c8c902d5d 100644 --- a/crates/workshop-server/src/backoff.rs +++ b/crates/workshop-server/src/backoff.rs @@ -34,7 +34,7 @@ const MAX_DELAY: Duration = Duration::from_secs(60); /// rqbit. At the 60s ceiling this is more than a day of continuous /// outage; a workshop whose gateway has been gone that long stops /// probing and says so. -const TOTAL_DELAY_BUDGET: Duration = Duration::from_secs(24 * 60 * 60); +const TOTAL_DELAY_BUDGET: Duration = Duration::from_hours(24); /// Shared reconnect-backoff state; clones feed one schedule. /// diff --git a/crates/workshop/Cargo.toml b/crates/workshop/Cargo.toml index 382ccd44d..4e27a9220 100644 --- a/crates/workshop/Cargo.toml +++ b/crates/workshop/Cargo.toml @@ -2,7 +2,6 @@ name = "workshop" version.workspace = true edition.workspace = true -rust-version.workspace = true license.workspace = true repository.workspace = true publish = false diff --git a/crates/workshop/src/bridge.rs b/crates/workshop/src/bridge.rs index 0d5469bbc..9b6d1de59 100644 --- a/crates/workshop/src/bridge.rs +++ b/crates/workshop/src/bridge.rs @@ -202,8 +202,7 @@ fn dropped_paths(args: &ICoreWebView2WebMessageReceivedEventArgs) -> Vec key.toLowerCase() === name.toLowerCase(), - ); -} - -function setEnvironmentVariable(environment, name, value) { - const key = environmentKey(environment, name) ?? name; - environment[key] = value; -} - -function deleteEnvironmentVariable(environment, name) { - const key = environmentKey(environment, name); - if (key) { - delete environment[key]; - } -} - -function createToolLayout(names = ["cargo", "rustc"]) { - const bin = mkdtempSync(join(fixtureRoot, "bin-")); - for (const name of names) { - const destination = join(bin, `${name}.exe`); - copyFileSync(fakeTool, destination); - chmodSync(destination, 0o755); - } - return bin; -} - -function runValidator({ - bin, - cargoVersion = "1.89.0", - contract = bin, - rustcVersion = "1.89.0", -} = {}) { - const environment = { ...process.env }; - const runRoot = mkdtempSync(join(fixtureRoot, "run-")); - const githubPath = join(runRoot, "github-path"); - const invocationLog = join(runRoot, "invocations"); - - deleteEnvironmentVariable(environment, "PROMPTFORGE_RUST_1_89_0_BIN"); - if (contract !== undefined) { - setEnvironmentVariable( - environment, - "PROMPTFORGE_RUST_1_89_0_BIN", - contract, - ); - } - setEnvironmentVariable(environment, "GITHUB_PATH", githubPath); - setEnvironmentVariable(environment, "FAKE_INVOCATION_LOG", invocationLog); - setEnvironmentVariable(environment, "FAKE_CARGO_VERSION", cargoVersion); - setEnvironmentVariable(environment, "FAKE_RUSTC_VERSION", rustcVersion); - - const powershell = process.platform === "win32" - ? join( - process.env.SystemRoot ?? "C:\\WINDOWS", - "System32", - "WindowsPowerShell", - "v1.0", - "powershell.exe", - ) - : "pwsh"; - const result = spawnSync( - powershell, - ["-NoProfile", "-NonInteractive", "-File", validator], - { - cwd: root, - encoding: "utf8", - env: environment, - timeout: 30_000, - }, - ); - return { - ...result, - githubPath, - invocationLog, - output: `${result.stdout ?? ""}${result.stderr ?? ""}`.replace(/\s+/g, " "), - }; -} - -before(() => { - writeFileSync( - fakeToolSource, - String.raw`use std::env; -use std::fs::OpenOptions; -use std::io::Write; - -fn main() { - let name = env::current_exe() - .expect("current executable") - .file_stem() - .expect("executable stem") - .to_string_lossy() - .to_ascii_lowercase(); - let arguments: Vec<_> = env::args().skip(1).collect(); - let mut log = OpenOptions::new() - .append(true) - .create(true) - .open(env::var("FAKE_INVOCATION_LOG").expect("invocation log")) - .expect("open invocation log"); - writeln!(log, "{name} {}", arguments.join(" ")).expect("write invocation log"); - let version = match name.as_str() { - "cargo" => env::var("FAKE_CARGO_VERSION").expect("cargo version"), - "rustc" => env::var("FAKE_RUSTC_VERSION").expect("rustc version"), - _ => panic!("unexpected fake tool name: {name}"), - }; - println!("{name} {version} (fixture 2026-09-08)"); -} -`, - ); - const compiled = spawnSync("rustc", [fakeToolSource, "-o", fakeTool], { - cwd: root, - encoding: "utf8", - timeout: 30_000, - }); - assert.equal( - compiled.status, - 0, - `failed to compile fake Rust tools:\n${compiled.stdout}${compiled.stderr}`, - ); -}); - -after(() => { - rmSync(fixtureRoot, { force: true, recursive: true }); -}); - -test("accepts valid direct Rust tools", () => { - const bin = createToolLayout(); - const result = runValidator({ bin }); - - assert.equal(result.status, 0, result.output); - assert.match(result.stdout, /Validated cargo 1\.89\.0 from /); - assert.match(result.stdout, /Validated rustc 1\.89\.0 from /); - assert.equal(readFileSync(result.githubPath, "utf8").trim(), resolve(bin)); -}); - -test("never passes a toolchain selector to either executable", () => { - const bin = createToolLayout(); - const result = runValidator({ bin }); - - assert.equal(result.status, 0, result.output); - assert.deepEqual( - readFileSync(result.invocationLog, "utf8").trim().split(/\r?\n/), - ["cargo --version", "rustc --version"], - ); -}); - -test("rejects an unset contract instead of discovering Rust", () => { - const result = runValidator({ contract: undefined }); - - assert.notEqual(result.status, 0); - assert.match(result.output, /PROMPTFORGE_RUST_1_89_0_BIN must be set/); -}); - -test("rejects relative and nonexistent contract directories", () => { - for (const [contract, message] of [ - [join("relative", "rust-bin"), /must be an absolute directory/], - [join(fixtureRoot, "does-not-exist"), /directory does not exist/], - ]) { - const result = runValidator({ contract }); - assert.notEqual(result.status, 0); - assert.match(result.output, message); - } -}); - -test("rejects missing or non-file executables", () => { - const missingRustc = createToolLayout(["cargo"]); - const nonFileCargo = createToolLayout(["rustc"]); - mkdirSync(join(nonFileCargo, "cargo.exe")); - - for (const bin of [missingRustc, nonFileCargo]) { - const result = runValidator({ bin }); - assert.notEqual(result.status, 0); - assert.match(result.output, /must contain regular cargo\.exe and rustc\.exe files/); - } -}); - -test("rejects malformed tool version output", () => { - for (const versions of [ - { cargoVersion: "stable" }, - { rustcVersion: "unknown" }, - ]) { - const result = runValidator({ bin: createToolLayout(), ...versions }); - assert.notEqual(result.status, 0); - assert.match(result.output, /returned malformed version output/); - } -}); - -test("rejects every wrong or mixed Rust version", () => { - for (const versions of [ - { cargoVersion: "1.90.0" }, - { rustcVersion: "1.88.0" }, - { cargoVersion: "1.89.0", rustcVersion: "1.90.0" }, - ]) { - const result = runValidator({ bin: createToolLayout(), ...versions }); - assert.notEqual(result.status, 0); - assert.match(result.output, /requires exactly 1\.89\.0/); - } -}); diff --git a/tools/validate-rust-1.89.0.ps1 b/tools/validate-rust-1.89.0.ps1 deleted file mode 100644 index 885ebef75..000000000 --- a/tools/validate-rust-1.89.0.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -$ErrorActionPreference = 'Stop' - -$contractName = 'PROMPTFORGE_RUST_1_89_0_BIN' -$requiredVersion = '1.89.0' -$contractBin = [Environment]::GetEnvironmentVariable($contractName) - -if ([string]::IsNullOrWhiteSpace($contractBin)) { - throw "$contractName must be set to an absolute Rust $requiredVersion bin directory" -} - -$contractBin = $contractBin.Trim() -if (-not [IO.Path]::IsPathRooted($contractBin)) { - throw "$contractName must be an absolute directory: '$contractBin'" -} -if (-not (Test-Path -LiteralPath $contractBin -PathType Container)) { - throw "$contractName directory does not exist: '$contractBin'" -} -$contractBin = (Resolve-Path -LiteralPath $contractBin).Path - -$tools = @{} -foreach ($name in @('cargo', 'rustc')) { - $path = Join-Path $contractBin "$name.exe" - if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { - throw "$contractName must contain regular cargo.exe and rustc.exe files: '$contractBin'" - } - $item = Get-Item -LiteralPath $path - if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { - throw "$contractName must contain regular cargo.exe and rustc.exe files: '$contractBin'" - } - $tools[$name] = $item.FullName -} - -function Read-RustVersion { - param( - [Parameter(Mandatory = $true)][string] $Name, - [Parameter(Mandatory = $true)][string] $Path - ) - - $lines = @(& $Path '--version' 2>&1) - $exitCode = $LASTEXITCODE - $text = ($lines | Out-String).Trim() - if ($exitCode -ne 0) { - throw "$Name.exe failed at '$Path' with exit code ${exitCode}: $text" - } - $match = [regex]::Match( - $text, - "^$([regex]::Escape($Name))\s+(\d+\.\d+\.\d+)(?:\s|$)" - ) - if (-not $match.Success) { - throw "$Name.exe returned malformed version output at '$Path': $text" - } - $actualVersion = $match.Groups[1].Value - if ($actualVersion -ne $requiredVersion) { - throw "$Name.exe reports $actualVersion, but $contractName requires exactly $requiredVersion" - } - Write-Host "Validated $Name $actualVersion from $Path" -} - -Read-RustVersion -Name 'cargo' -Path $tools['cargo'] -Read-RustVersion -Name 'rustc' -Path $tools['rustc'] - -if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_PATH)) { - $contractBin | Add-Content -LiteralPath $env:GITHUB_PATH -} - -exit 0 diff --git a/vibe-ledger.md b/vibe-ledger.md index 6a51f8f50..d4994fceb 100644 --- a/vibe-ledger.md +++ b/vibe-ledger.md @@ -68,3 +68,7 @@ - Rulebook debt tiers Step 5: FixtureError and typed sources in the take/finalization pipeline - focused verification: `cargo test -p gateway-stt -F test-fixtures` (125 lib + 53 integration, 0 failed), clippy `-D warnings`, fmt, and feature-off check green; review clean; no Verify dispatch (mid-component step with no fixes). Decision: take failure stored and shared as `Arc` since `pending_failure` clones it out of the mutex for session gating | Falsifier: if the failure were only ever moved, plain `TakeFailure` channels suffice. Decision: `PendingPrecommitFailure` carries `Arc` without `#[source]`; thiserror 2.0.19 `AsDynError` has no `Arc` impl | Falsifier: a thiserror release supporting Arc sources. Decision: `FixtureError` boxes pub(crate) sources as `Box`; `SpeechError`/`serde_json::Error` carried concretely | Falsifier: making `SessionError`/`RegisterError`/`ClientError` public. Decision: `ItemFailure` stays String-carrying (Clone+Eq wire terminal) but is classified from the typed `TakeFailure` | Falsifier: the wire protocol gains typed failure codes. - Rulebook debt tiers Step 6: anyhow for test and build code, typed errors in remaining production - FULL gate pass: build, fmt, clippy (pinned and newer stable), docs, full workspace tests, workshop nextest, and doctests all green (verify-step-6-round-1.log); review clean including the cumulative component diff. Decision: build-ui uses anyhow, not a new thiserror type | Falsifier: the contract allows exactly one new public error type (FixtureError) and routes build tooling to anyhow. Decision: menu.rs gains private `SwitchFailure` with Display byte-identical to the old strings | Falsifier: no existing workshop-server enum models switch lifecycle. Decision: dialect.rs gains private `ToolCallRejection` with Display matching the old wire warnings exactly | Falsifier: the reasons become `gateway_warning` wire strings, so the text had to survive. Decision: confine.rs `parse_whoami_user_sid` returns `LocalError::CacheNotPrivate` directly | Falsifier: keeps the existing variant and message while dropping the String channel. Decision: the two LazyLock statics hold `SharedSource`, replayed via a new `Error::shared` helper | Falsifier: SharedSource exists precisely for re-producing typed errors from non-Clone caches. + +- Unify toolchain Step 1: Unify toolchain, fix the three CI failures, and remove MSRV machinery - focused verify pass: `cargo build` and `cargo nextest run -p gateway-stt-engine --all-features decode_rendezvous_timeout` green (verify-step-1-round-1.log); both clippy scopes and fmt clean during coding. Decision: rewrote the `rust-toolchain.toml` comment, which described the removed MSRV pin | Falsifier: the old comment claims a pin and CI overrides that no longer exist. Decision: left "Rust 1.89 or later" in five crate README.md files untouched (outside the step's enumerated files) | Falsifier: plan step or contract names them. Decision: split four out-of-scope `Duration::from_mins`/`from_hours` hunks into a separate commit (`c1ef217f`) rather than amending the step to name them | Falsifier: the plan file is treated as amendable after review, making the split commit redundant history. + +- Unify toolchain Step 2: Full verification gate and CI confirmation - local gate run and the fixes pushed to origin/master (`23ffa642`); the `native-whisper` self-hosted lane failed on provisioning (the runner's NetworkService rustup home had only 1.89), fixed by junctioning its `stable-x86_64-pc-windows-msvc` toolchain directory to the operator's profile copy with a read ACL, so the runner tracks the operator's stable from now on. The operator closed the run assuming the in-flight CI passes rather than watching it. Falsifier: the CI run on `23ffa642`. diff --git a/vibe/2026-09-11-2-unify-toolchain.md b/vibe/2026-09-11-2-unify-toolchain.md new file mode 100644 index 000000000..dea7f338d --- /dev/null +++ b/vibe/2026-09-11-2-unify-toolchain.md @@ -0,0 +1,157 @@ +--- +name: Unify toolchain and fix CI +overview: Remove the MSRV/local-vs-CI toolchain split so local builds and CI use the same stable Rust, and fix the three CI failures the skew caused. +todos: + - id: fix-ci-failures + content: Fix audio.rs:317 parens, bridge.rs expect lints, decode.rs flaky test + status: pending + - id: unify-toolchain-files + content: rust-toolchain.toml to stable, remove rust-version from workspace and all 34 crates + status: pending + - id: simplify-ci-yml + content: Remove RUSTUP_TOOLCHAIN overrides and msrv job from ci.yml + status: pending + - id: simplify-other-workflows + content: Remove MSRV env and validation step from stt-miri.yml, remove from release-workshop.yml + status: pending + - id: docs-and-cleanup + content: Remove skew bullet from AGENTS.md, MSRV section from README.md, delete validation script and its test + status: pending + - id: verify + content: Run full gate locally on stable, push, confirm CI green + status: pending +isProject: false +--- + +# Unify toolchain and fix CI + + + +## Product Requirements + +The repository pins local builds to Rust 1.89 while CI lints and tests on stable (currently 1.98). This skew just caused three CI failures on a pull request that passed every local check. Every crate in the workspace is `publish = false`, so no downstream consumer needs an MSRV guarantee. + +- Problem and users: the developer (and their self-hosted runner, which is the same machine) cannot trust that green locally means green on CI. +- Goals: local builds and CI use the same Rust toolchain; the three current CI failures are fixed; the MSRV machinery is removed. +- Non-goals: changing the Miri nightly pin (that pins a specific nightly for diagnostic text stability, a different concern); supporting consumers who build from crates.io (there are none). +- Success criteria: the full verification gate passes locally on stable and CI goes green on the same toolchain. +- Constraints: the self-hosted runner must track stable without manual intervention after the change; the `rust-version` field's resolver side effect (preferring dependency versions compatible with the declared MSRV) is accepted as going away. +- Open questions: none. + +## Functional Specification + +The developer runs the same commands locally that CI runs, on the same toolchain, so a green local gate predicts a green CI run instead of merely suggesting it. The three current CI failures disappear as a side effect of removing the skew that produced them, not as patches layered on top of it. A contributor cloning the repository gets the right toolchain from rustup automatically - no version number to look up, no MSRV policy to learn, no validation script to run. + +- Actors and workflows: the developer builds and tests locally; CI runs the same checks on every pull request. +- Inputs and outputs: code changes go in; a green or red CI status comes out. +- States and validation: before, local and CI can disagree on lint and test outcomes; after, they cannot disagree on toolchain behavior. +- Errors and recovery: the three current failures (a clippy lint, a stale lint expectation, a flaky paused-time test) are fixed in place. +- Security and privacy behavior: none. +- Acceptance criteria: `cargo fmt --all --check`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, `cargo test --locked --workspace --all-features`, `cargo test --doc`, and `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features` all pass locally on stable, and the CI run on the same commit is green. + + + + +## Technical Design + +The change removes the MSRV pin and its CI overrides, then fixes the three code issues the skew exposed. + +- `rust-toolchain.toml`: `channel = "1.89"` becomes `channel = "stable"`. +- `Cargo.toml`: remove `rust-version = "1.89"` from `[workspace.package]`. +- All 34 crate manifests under `crates/`: remove `rust-version.workspace = true` (line 5 of each). +- `.github/workflows/ci.yml`: remove the global `env.RUSTUP_TOOLCHAIN: stable` (line 19) and every per-job `env.RUSTUP_TOOLCHAIN: stable` (lines 37, 87, 136, 166, 238); delete the entire `msrv` job (lines 344-372); remove `msrv` from `ci-green`'s `needs:` list (line 394). +- `.github/workflows/stt-miri.yml`: remove `env.RUSTUP_TOOLCHAIN: 1.89` and `env.RUSTUP_AUTO_INSTALL: "0"` from `native-whisper` (lines 49-51); remove the "Verify preinstalled MSRV Rust" step (lines 55-57). The Miri job's `nightly-2026-09-05` pin stays. +- `.github/workflows/release-workshop.yml`: remove the MSRV comment (lines 30-31) and `RUSTUP_TOOLCHAIN: stable` (line 32). +- `AGENTS.md`: remove the last bullet about CI linting on a newer stable than the pinned local MSRV. +- `README.md`: remove the "Minimum Rust Version" section (lines 112-114); change "Every build needs Rust 1.89 or later and Node.js 22" to "Every build needs Rust and Node.js 22" (line 68); remove or update the `rust-1.89+` badge (line 3). +- Delete `tools/validate-rust-1.89.0.ps1` and `tools/check-stt-native-workflow.test.mjs` (the test exists only to test the script). +- `crates/gateway-stt/src/audio.rs:317`: remove unnecessary parens - `<(dyn std::error::Error + 'static)>::is::<...>` becomes `::is::<...>`. +- `crates/workshop/src/bridge.rs`: replace `#[expect(clippy::ptr_as_ptr, clippy::borrow_as_ptr)]` with `#[expect(clippy::ref_as_ptr)]` on the `#[implement(...)]` macro blocks (the old lints were replaced by `ref_as_ptr` in Rust 1.98). +- `crates/gateway-stt-engine/src/test_fixtures/tests/scenario_cleanup/decode.rs`: revert the `decode_rendezvous_timeout` test to real-time (remove `start_paused = true`); the paused clock is structurally incompatible with the test's real-time condvar rendezvous. + + + + +## Testing Plan + +The full verification gate runs locally on stable after the changes, then CI confirms on the same toolchain. + +- Unit: the existing suite passes unmodified except for the three fixed files. +- Integration and end-to-end: the full workspace test suite passes on stable. +- Regression, security, and performance: the three CI failures are fixed and do not recur; the `ci-green` aggregate job reflects the remaining jobs accurately after `msrv` is removed from its `needs:` list. +- Exit criteria: `cargo fmt --all --check`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, `cargo test --locked --workspace --all-features`, `cargo test --doc`, and `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features` all pass locally on stable, and the CI run on the same commit is green. + + + + +## Decision Record + +- Decisions: + - Unify on `stable` rather than pinning to an exact version like `1.98`: "I want a rule: local builds and CI use the same environment. I never want CI to fail when local succeeds." Pinning to an exact version would require periodic bumps; `stable` tracks automatically. + - Remove `rust-version` entirely rather than setting it to match stable: "should we get rid of all this msrv and surrounding crap? I want a dramatic simplification to the build and CI." Every crate is `publish = false`, so the field has no consumer audience. + - Delete the `msrv` CI job rather than keeping it as a lower-bound check: the job exists to verify the MSRV, which no longer exists. + - Revert the `decode_rendezvous_timeout` test to real-time rather than fixing the paused-clock interaction: the test's condvar rendezvous runs on real OS threads, making paused time structurally incompatible. +- Rejected alternatives: + - Pin to an exact stable version (e.g., `1.98`): rejected because it requires manual bumps and reintroduces a drift window; revisit if a future stable release breaks the build in a way that needs pinning. + - Keep `rust-version` set to the current stable: rejected because it would drift and serve no consumer; revisit if the project ever publishes a crate. +- Assumptions, risks, and notes: + - The self-hosted runner needs `rustup default stable` run once after this change; after that, rustup tracks stable automatically. + - Removing `rust-version` means Cargo's resolver may select dependency versions that require newer Rust than any previously declared MSRV; on stable, this is always satisfied. + - A contributor on an old Rust who clones the repo gets `stable` from rustup automatically via the toolchain file; no compile error, no manual install. + + + + +## Project Survey + +- Status: complete +- Build command: `cargo build` (builds the gateway, the default member); `cargo build -p workshop` for the desktop app (explicit opt-in; needs Tauri system packages and `npm ci --prefix crates/workshop-server/ui` plus `npm ci --prefix crates/gateway-config-ui/ui` first) +- Focused test command pattern: `cargo nextest run -p ` or `cargo test -p --test it ` (integration binaries are named `it`) +- Component test command pattern: `cargo nextest run -p ` (all features: add `--all-features`) +- Full-suite test command: `cargo nextest run --locked --workspace --exclude workshop --exclude workshop-server --all-features`, then `cargo test --workspace --exclude workshop --exclude workshop-server --all-features --doc` for doctests; workshop crates run separately with `cargo nextest run --locked -p workshop -p workshop-server` +- Linter command: `cargo clippy --workspace --exclude workshop --exclude workshop-server --all-targets --all-features -- -D warnings`; workshop crates: `cargo clippy -p workshop -p workshop-server --all-targets -- -D warnings` +- Formatter check command: `cargo fmt --all --check` +- Docs command: `cargo doc --workspace --no-deps --all-features --exclude workshop --exclude workshop-server` with `RUSTDOCFLAGS: -D warnings` +- Test placement and naming conventions: unit tests live in `src/tests.rs` or `#[cfg(test)]` modules beside the code; integration tests live in `tests/it/` as one binary (`main.rs` plus per-area modules such as `boot.rs`, `chat.rs`); behavior tests ship in the same change as behavior changes +- Directory map: `crates/` holds all workspace members (Rust crates plus `shared-ui`, a TypeScript+CSS package excluded from the Cargo glob); `guide/` holds the user guide sources; `prompts/` holds prompt pipelines; `tools/` holds Node maintenance scripts (sidecar staging, workflow checks); `vibe/` holds `archdoc.md` architecture documentation; `images/` and `local/` hold assets and local config; `.github/workflows/` holds CI; `target/` and `target-msrv/` are build outputs +- Component boundaries: executor (`promptforge`, `promptforge-core`, `promptforge-lua`, `promptforge-parser`, `promptforge-agent`, `promptforge-tools`, `promptforge-store`, `promptforge-webfetch`, `promptforge-web-search`, `promptforge-model-client`, `promptforge-tool-picker`) executes pipelines and Lua programs; `gateway*` crates own model routing, provider access, STT, and local inference as an independent server; `workshop` and `workshop-server` are the Tauri desktop shell and in-process server; `shared-*` crates are the dependency-free substrate (progress, loopback, protocol, sidecar). Dependency rules: PromptForge crates cannot depend on Gateway or Workshop crates; Gateway crates cannot depend on PromptForge or Workshop crates; Workshop crates cannot depend on Gateway crates +- Conventions summary: Rust edition 2024, MSRV 1.89 pinned in `rust-toolchain.toml` while CI lints and tests on stable; `unsafe_code` forbidden workspace-wide with documented exceptions at owned boundaries; clippy `all` denied, `pedantic` warned, `unwrap_used`/`expect_used` denied; long-running work reports through `shared-progress`; comments explain non-obvious constraints and cite upstream issue URLs for workarounds; UI bundles build into `OUT_DIR` and no build step may dirty the repository tree + + + + +## Execution Instructions + + + +### Step 1: Unify toolchain, fix the three CI failures, and remove MSRV machinery [completed] + +- Component: `none` + +Apply every change in one commit (code fixes plus their tests, per repo convention that behavior tests ship with behavior changes): + +- `crates/gateway-stt/src/audio.rs:317`: remove unnecessary parens - `<(dyn std::error::Error + 'static)>::is::<...>` becomes `::is::<...>`. +- `crates/workshop/src/bridge.rs`: replace `#[expect(clippy::ptr_as_ptr, clippy::borrow_as_ptr)]` with `#[expect(clippy::ref_as_ptr)]` on the `#[implement(...)]` macro blocks. +- `crates/gateway-stt-engine/src/test_fixtures/tests/scenario_cleanup/decode.rs`: revert `decode_rendezvous_timeout` to real-time (remove `start_paused = true`). +- `rust-toolchain.toml`: `channel = "1.89"` becomes `channel = "stable"`. +- `Cargo.toml`: remove `rust-version = "1.89"` from `[workspace.package]`; remove `rust-version.workspace = true` from all 34 crate manifests under `crates/`. +- `.github/workflows/ci.yml`: remove global `env.RUSTUP_TOOLCHAIN: stable` (line 19) and per-job overrides (lines 37, 87, 136, 166, 238); delete the `msrv` job (lines 344-372); remove `msrv` from `ci-green`'s `needs:` list (line 394). +- `.github/workflows/stt-miri.yml`: remove `env.RUSTUP_TOOLCHAIN: 1.89` and `env.RUSTUP_AUTO_INSTALL: "0"` (lines 49-51) and the "Verify preinstalled MSRV Rust" step (lines 55-57); keep the `nightly-2026-09-05` Miri pin. +- `.github/workflows/release-workshop.yml`: remove the MSRV comment (lines 30-31) and `RUSTUP_TOOLCHAIN: stable` (line 32). +- `AGENTS.md`: remove the last bullet about CI linting on a newer stable than the pinned local MSRV. +- `README.md`: remove the "Minimum Rust Version" section (lines 112-114); change line 68 to "Every build needs Rust and Node.js 22"; remove or update the `rust-1.89+` badge (line 3). +- Delete `tools/validate-rust-1.89.0.ps1` and `tools/check-stt-native-workflow.test.mjs`. + + + + + +### Step 2: Run the full verification gate on stable and confirm green CI [completed] + +- Component: `none` + +Run once on the self-hosted runner: `rustup default stable`. Then locally on stable, in order: `cargo fmt --all --check`; `cargo clippy --workspace --all-targets --all-features -- -D warnings`; `cargo test --locked --workspace --all-features`; `cargo test --doc`; `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features`. All must pass. Commit any residual fixes, push, and confirm the CI run on the same commit is green, with `ci-green` reflecting the remaining jobs after `msrv` removal. + + + +