quiver-dsp 0.1.0

A modular audio synthesis library using Arrow-style combinators and graph-based patching
Documentation
name: CI

on:
  push:
    branches: [main, master]
  pull_request:
    branches: [main, master]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always

jobs:
  # Fast lint checks - single job to reduce setup overhead
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
      - name: Format
        run: cargo fmt --all -- --check
      - name: Clippy
        run: cargo clippy --all-features -- -D warnings

  # Main test job
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Run tests
        run: cargo test --all-features

  # Examples - only when source or examples change
  examples:
    name: Examples
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Build and run examples
        run: |
          cargo build --examples --all-features
          # Run each example in the foreground so `set -e` fails the step on any
          # runtime panic. A bare `wait` on backgrounded jobs always returns 0
          # and would silently swallow a panicking example.
          cargo run --example quick_taste
          cargo run --example first_patch
          cargo run --example tutorial_subtractive
          cargo run --example tutorial_envelope
          cargo run --example tutorial_filter_mod

  # Documentation - check rustdoc warnings
  doc:
    name: Docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Build docs
        run: cargo doc --no-deps --all-features
        env:
          RUSTDOCFLAGS: -D warnings

  # no_std / alloc-tier check (Q137): actually exercise the feature tiers that
  # src/lib.rs documents ("Without any features, the library operates in
  # no_std mode with alloc, providing core DSP modules for embedded systems
  # and WebAssembly targets") on a genuine no_std target, instead of only ever
  # building with --all-features like every other job here. Cheap and PR-gated
  # since it's just two `cargo rustc --emit=metadata` invocations.
  #
  # `--crate-type rlib` overrides the crate's declared `cdylib` (Cargo.toml,
  # required by wasm-pack, see the [lib] comment there / Q140): checking the
  # unmodified cdylib+rlib crate-type on a no_std build fails with spurious
  # "no global memory allocator" / "panic_handler function required" errors
  # that have nothing to do with this crate's actual no_std code, because
  # rustc requires an allocator/panic-handler for any cdylib artifact.
  #
  # thumbv7em-none-eabihf (Cortex-M4/M7) has no native 64-bit atomic load/store
  # (`core`'s max_atomic_width is 32 there), so `core::sync::atomic::AtomicU64`
  # does not exist on this target. The crate handles this by depending on
  # `portable-atomic` (Cargo.toml) and using `portable_atomic::AtomicU64` in
  # src/polyphony.rs, src/io.rs, and src/rng.rs instead of the `core` type.
  # Both commands below therefore compile cleanly on this target; this job is a
  # real (non-`continue-on-error`) gate that keeps the no_std build honest in CI.
  no_std:
    name: no_std / alloc
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: thumbv7em-none-eabihf
      - uses: Swatinem/rust-cache@v2
      - name: Check no_std (bare, no default features)
        run: cargo rustc --lib --crate-type rlib --no-default-features --target thumbv7em-none-eabihf -- --emit=metadata
      - name: Check no_std + alloc
        run: cargo rustc --lib --crate-type rlib --no-default-features --features alloc --target thumbv7em-none-eabihf -- --emit=metadata

  # Browser tests - WASM integration tests with Playwright
  browser-tests:
    name: Browser Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown
      - uses: Swatinem/rust-cache@v2
      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: demos/browser/tests/package-lock.json
      - name: Install Playwright
        run: |
          cd demos/browser/tests
          npm ci
          npx playwright install --with-deps chromium
      - name: Run browser tests
        run: make test-browser

  # ============================================================================
  # Main branch only - expensive or comparative checks
  # ============================================================================

  # MSRV check - only on main (PRs should just work if main works)
  msrv:
    name: MSRV (1.78)
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@1.78.0
      - uses: Swatinem/rust-cache@v2
      - name: Check MSRV
        run: cargo check --all-features

  # Benchmarks - only meaningful on main for comparisons
  bench:
    name: Benchmarks
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      # Compile the benches both ways so the scalar and SIMD (wide-backed) code
      # paths are guaranteed to build.
      - name: Compile benchmarks (scalar + SIMD)
        run: |
          cargo bench --no-run
          cargo bench --no-run --features simd
      # Actually gate the real-time deadline (Q114). Must run in release: the
      # test's wall-clock assertion is only active in optimized builds.
      - name: Real-time compliance gate (release)
        run: cargo test --release --test realtime_compliance -- --nocapture
      # Produce criterion measurement data with short warm-up/measurement times
      # so the run stays bounded, then persist it as a downloadable baseline.
      - name: Run benchmarks (generate criterion baselines)
        run: cargo bench -- --warm-up-time 1 --measurement-time 3
      - name: Upload criterion baselines
        uses: actions/upload-artifact@v4
        with:
          name: criterion-baselines
          path: target/criterion
          if-no-files-found: warn

  # Coverage - expensive, main only
  coverage:
    name: Coverage
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - uses: Swatinem/rust-cache@v2
      - name: Install cargo-llvm-cov
        uses: taiki-e/install-action@cargo-llvm-cov
      - name: Run coverage
        run: |
          cargo llvm-cov --all-features \
            --ignore-filename-regex 'src/wasm/.*' \
            --fail-under-lines 80