midstream 0.2.0

Real-time LLM streaming with inflight analysis
Documentation
name: Rust CI/CD

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # Code quality checks
  format:
    name: Format Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt

      - name: Check formatting
        run: cargo fmt --all -- --check

  clippy:
    name: Clippy Lints
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "clippy"

      - name: Run clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

  # MSRV (Minimum Supported Rust Version) check per ADR-0023.
  # Every published crate declares rust-version = "1.81"; this job
  # proves the workspace still compiles + tests on that toolchain so
  # the declared MSRV doesn't quietly drift forward. --locked is
  # mandatory: an unlocked update could pull a transitive dep that
  # requires newer rustc, masking the MSRV check.
  msrv:
    name: MSRV (1.81)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust 1.81 toolchain
        uses: dtolnay/rust-toolchain@1.81

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "msrv-1.81"

      - name: cargo check (workspace, locked)
        # Exclude `midstream` and `hyprstream` while ADR-0002 / ADR-0014
        # un-vendoring is in flight: those two crates currently fail to
        # build on any toolchain due to arrow-schema version skew via
        # the vendored hyprstream-main. Re-include both once ADR-0002
        # lands. The 6 published `midstreamer-*` crates plus the WASM
        # bindings are covered.
        run: cargo check --workspace --exclude midstream --exclude hyprstream --locked

      - name: cargo test --lib (workspace, locked)
        run: cargo test --workspace --exclude midstream --exclude hyprstream --lib --locked

  # Build and test matrix
  test:
    name: Test (${{ matrix.os }}, ${{ matrix.rust }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        rust: [stable, nightly]
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
          - os: macos-latest
            target: x86_64-apple-darwin
          - os: windows-latest
            target: x86_64-pc-windows-msvc

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}
          targets: ${{ matrix.target }}

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "test-${{ matrix.os }}-${{ matrix.rust }}"

      - name: Build workspace
        run: cargo build --workspace --all-features --verbose

      - name: Run unit tests
        run: cargo test --workspace --all-features --lib --verbose

      - name: Run integration tests
        run: cargo test --workspace --all-features --test '*' --verbose

      - name: Run doc tests
        run: cargo test --workspace --all-features --doc --verbose

  # Individual crate builds
  build-crates:
    name: Build Crates
    runs-on: ubuntu-latest
    strategy:
      matrix:
        crate:
          - temporal-compare
          - nanosecond-scheduler
          - temporal-attractor-studio
          - temporal-neural-solver
          - strange-loop

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "crate-${{ matrix.crate }}"

      - name: Build ${{ matrix.crate }}
        run: cargo build -p ${{ matrix.crate }} --all-features --verbose

      - name: Test ${{ matrix.crate }}
        run: cargo test -p ${{ matrix.crate }} --all-features --verbose

  # WASM builds
  wasm:
    name: WASM Build
    runs-on: ubuntu-latest
    strategy:
      matrix:
        crate:
          - temporal-compare
          - nanosecond-scheduler
          - strange-loop

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "wasm"

      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

      - name: Build ${{ matrix.crate }} for WASM
        run: cargo build -p ${{ matrix.crate }} --target wasm32-unknown-unknown --no-default-features

  # Benchmarks
  benchmark:
    name: Performance Benchmarks
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "bench"

      - name: Run benchmarks
        run: cargo bench --workspace --all-features -- --output-format bencher | tee benchmark-results.txt

      - name: Store benchmark results
        uses: benchmark-action/github-action-benchmark@v1
        if: github.event_name == 'push'
        with:
          tool: 'cargo'
          output-file-path: benchmark-results.txt
          github-token: ${{ secrets.GITHUB_TOKEN }}
          auto-push: true
          alert-threshold: '150%'
          comment-on-alert: true
          fail-on-alert: false

  # Documentation
  docs:
    name: Documentation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "docs"

      - name: Build documentation
        run: cargo doc --workspace --all-features --no-deps
        env:
          RUSTDOCFLAGS: "-D warnings"

      - name: Deploy documentation
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./target/doc
          destination_dir: docs

  # Security audit
  security:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Install cargo-audit
        run: cargo install cargo-audit

      - name: Run security audit
        run: cargo audit --deny warnings

  # Code coverage
  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Setup cache
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: "coverage"

      - name: Install cargo-tarpaulin
        run: cargo install cargo-tarpaulin

      - name: Generate coverage
        run: cargo tarpaulin --workspace --all-features --out Xml --output-dir coverage

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v6
        with:
          files: ./coverage/cobertura.xml
          fail_ci_if_error: false

  # All checks passed
  ci-success:
    name: CI Success
    if: always()
    needs:
      - format
      - clippy
      - msrv
      - test
      - build-crates
      - wasm
      - docs
      - security
    runs-on: ubuntu-latest
    steps:
      - name: Check all jobs
        run: |
          if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
            echo "One or more jobs failed"
            exit 1
          fi
          echo "All CI jobs passed!"