neuromod 0.5.2

Biologically inspired SNN primitives in Rust: LIF/Izhikevich SpikingNetwork, neuromodulators, STDP building blocks, and standalone neuron models.
Documentation
# Third-party Actions are pinned to immutable commit SHAs (Aikido supply-chain policy).
name: CI

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

jobs:
  validate:
    name: Build & Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
          fetch-depth: 0

      - name: Install Rust 1.97.1
        uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # pinned action
        with:
          toolchain: 1.97.1
          components: clippy, rustfmt

      # Keep Cargo.toml rust-version, rust-toolchain.toml, and this job's
      # toolchain: string identical (MSRV single source of truth — #79).
      # ci_rv is parsed from the dtolnay/rust-toolchain `toolchain:` input above
      # so a bump of only that pin cannot silently pass this check.
      # shell: bash so the awk script runs on Windows runners too.
      - name: Verify MSRV pins agree
        shell: bash
        run: |
          set -euo pipefail
          cargo_rv=$(grep -m1 '^rust-version' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
          toolchain_ch=$(grep -m1 '^channel' rust-toolchain.toml | sed -E 's/.*"([^"]+)".*/\1/')
          # Extract from the Install Rust step's `toolchain:` input (not a hardcoded mirror).
          ci_rv=$(awk '
            /uses: dtolnay\/rust-toolchain@/ { in_rt = 1; next }
            in_rt && /^[[:space:]]*toolchain:[[:space:]]*/ {
              sub(/^[[:space:]]*toolchain:[[:space:]]*/, "")
              sub(/[[:space:]]*$/, "")
              print
              exit
            }
            in_rt && /^[[:space:]]*- name:/ { exit }
          ' .github/workflows/ci.yml)
          test -n "$ci_rv"
          echo "Cargo.toml rust-version=$cargo_rv"
          echo "rust-toolchain.toml channel=$toolchain_ch"
          echo "ci.yml pin=$ci_rv"
          test "$cargo_rv" = "$toolchain_ch"
          test "$cargo_rv" = "$ci_rv"

      - name: Rust Cache
        uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2

      - name: Check for relevant changes
        uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
        id: changes
        with:
          filters: |
            rust:
              - '**/Cargo.toml'
              - '**/Cargo.lock'
              - 'src/**'
              - 'examples/**'
              - 'benches/**'
              - 'tests/**'

      - name: Check formatting
        if: runner.os == 'Linux'
        run: cargo fmt --check

      - name: Clippy (lint)
        run: cargo clippy --all-targets --all-features -- -D warnings

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

      - name: Test (nextest for speed)
        if: steps.changes.outputs.rust == 'true'
        shell: bash
        run: |
          set -euo pipefail
          cargo install cargo-nextest --locked
          cargo nextest run --all-features --no-fail-fast

      - name: Feature matrix (cargo-hack)
        if: runner.os == 'Linux' && steps.changes.outputs.rust == 'true'
        run: |
          cargo install cargo-hack --locked
          cargo hack check --feature-powerset --exclude-no-default-features --keep-going

      - name: Verify docs are domain-agnostic
        if: runner.os == 'Linux'
        shell: bash
        run: |
          set -euo pipefail
          cargo doc --all-features --no-deps
          ! grep -riE 'spikenaut|\bhft\b|\bmining\b|\bcrypto\b|eagle-lander' target/doc/neuromod/