alice-physics 1.2.0

Deterministic 128-bit Fixed-Point Physics Engine
Documentation
name: CI

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

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        # v1.0 Item E Phase 2: 5 native platform matrix (WASM は独立 job で扱う)
        # - macos-latest       → aarch64-apple-darwin       (Mac ARM, primary dev, golden hash baseline)
        # - macos-15-intel     → x86_64-apple-darwin        (Mac Intel)
        # - ubuntu-latest      → x86_64-unknown-linux-gnu   (Linux x86)
        # - ubuntu-24.04-arm   → aarch64-unknown-linux-gnu  (Linux ARM、GA runner 2025-01+)
        # - windows-latest     → x86_64-pc-windows-msvc     (Windows x86)
        # + wasm-test job below → wasm32-wasip1 (WASI) via wasmtime
        os: [macos-latest, macos-15-intel, ubuntu-latest, ubuntu-24.04-arm, windows-latest]
    steps:
      - uses: actions/checkout@v5

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

      - uses: Swatinem/rust-cache@v2

      # inline stubs は composite action に集約 (canonical CI template [[reference_alice_ci_canonical_template]])
      # security-audit.yml / fuzz.yml と DRY で共有、license field 追加も一元管理
      - name: Create ALICE optional bridge stubs
        uses: ./.github/actions/alice-stubs

      # `crate-type` includes cdylib/staticlib (FFI consumers, 1.2.0); a no_std
      # cdylib needs a panic_handler + allocator, so the no_std *library* build is
      # checked as rlib only (same workaround as ALICE-ML). Downstream no_std
      # users always get the rlib.
      - name: Build (no_std, rlib)
        run: cargo rustc --lib --no-default-features --crate-type rlib

      - name: Build (std only)
        run: cargo build --lib --no-default-features --features "std"

      - name: Build (core — default features)
        run: cargo build --lib

      - name: Test (unit + integration + doc)
        run: cargo test

      # 1.2.0: the C ABI consumers (Unity P/Invoke, unreal-plugin/) link the
      # cdylib / staticlib — assert both are actually produced, not just the rlib.
      # the ffi test module (goldens the Unity / UE5 bindings replay) was never
      # executed in CI before 1.2.0 — only built
      - name: Test (FFI, lib)
        # quoted: a bare trailing `ffi::` makes YAML read the line as a mapping
        # (actionlint syntax-check; two pushes were red on this, 2026-09-15)
        run: 'cargo test --lib --features "ffi" "ffi::"'

      - name: Build (FFI) + assert cdylib / staticlib artifacts
        shell: bash
        run: |
          cargo build --features "ffi"
          ls target/debug/
          # `-f` tests, not `ls a b c | grep`: under `set -o pipefail` a missing
          # sibling name makes `ls` exit 2 and fails the pipeline even when the
          # platform's artifact is present (first run of this step, 2026-09-15).
          cdylib=0; for f in target/debug/libalice_physics.so target/debug/libalice_physics.dylib target/debug/alice_physics.dll; do [ -f "$f" ] && cdylib=1; done
          staticlib=0; for f in target/debug/libalice_physics.a target/debug/alice_physics.lib; do [ -f "$f" ] && staticlib=1; done
          [ "$cdylib" = 1 ] || { echo "no cdylib produced"; exit 1; }
          [ "$staticlib" = 1 ] || { echo "no staticlib produced"; exit 1; }

      - name: Build (parallel)
        run: cargo build --lib --features "parallel"

      - name: Test (parallel — unit + integration)
        run: cargo test --features "parallel"

      # full native feature set: the gpu-solver-bridge tests were only linted
      # (clippy), never run, before 1.2.0 — the quality-deep mutants job found
      # them failing in the unmutated tree
      - name: Test (full native feature set, lib)
        run: cargo test --lib --features "std,simd,parallel,ffi,gpu-solver-bridge"

      # test, not just build: a feature whose code is only compiled is a feature
      # whose effect nobody verifies (external review 2026-09-15: the SIMD paths
      # were scalar with dead intrinsics and no test caught the mismatch claim).
      - name: Test (SIMD, lib)
        run: cargo test --lib --features "simd"

  # v1.0 Item E Phase 2: WASM determinism check
  # `wasm32-wasip1` target で determinism_golden test を build → wasmtime で
  # 実行 SHA-256 hash が native platform (macOS ARM baseline) と bit-exact
  # 一致することを検証 Fix128 は純粋整数演算のため cross-platform bit-exact、
  # SDF 経路の f32 も IEEE 754 basic ops (+/-/*/sqrt) が platform 非依存
  sibling-features:
    name: Test (neural / replay / analytics via crates.io siblings)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: sibling-features
      # The bridge modules (alice-ml / alice-db / alice-analytics) come from
      # crates.io. Their contract tests never ran in CI before 1.2.0, which is
      # how alice-db 0.2.0-beta.1's archived-path stub (every replay read 0.0)
      # went unnoticed; fixed in alice-db 0.2.0-beta.2.
      - name: Test (lib, neural + replay + analytics)
        run: cargo test --lib --features "neural,replay,analytics"

  wasm-test:
    name: Test (wasm32-wasip1 via wasmtime)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Install Rust toolchain (with wasm32-wasip1)
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-wasip1

      # dtolnay/rust-toolchain の targets: が rustup target 追加を silent
      # fail することがある fallback として明示 rustup target add を実行
      # (idempotent、既に install 済なら no-op)
      - name: Ensure wasm32-wasip1 rust-std is installed
        run: rustup target add wasm32-wasip1

      - uses: Swatinem/rust-cache@v2
        with:
          shared-key: wasm-wasip1

      - name: Create ALICE optional bridge stubs
        uses: ./.github/actions/alice-stubs

      # 2026-09-15: `curl … | bash` は curl が network error (35 Connection reset) でも
      # pipeline exit が bash 側 = 0 になり step green のまま次 step で `wasmtime: command not found`
      # (exit 127) になる (run 34863693753) 公式 setup action に置換 (retry 内蔵、pipefail 問題が消える)
      - name: Install wasmtime
        uses: bytecodealliance/actions/wasmtime/setup@v1

      - name: Build determinism_golden test (wasm32-wasip1)
        run: cargo test --test determinism_golden --target wasm32-wasip1 --no-run

      - name: Run determinism_golden test via wasmtime
        run: |
          WASM_BIN=$(find target/wasm32-wasip1/debug/deps -name 'determinism_golden-*.wasm' -not -name '*.d' | head -1)
          echo "Executing: $WASM_BIN"
          wasmtime "$WASM_BIN"

      # v1.1.0: the f32/f64 field modules are pinned too (det_math + IEEE basic ops)
      - name: Build determinism_golden_f32 test (wasm32-wasip1)
        run: cargo test --test determinism_golden_f32 --target wasm32-wasip1 --no-run

      - name: Run determinism_golden_f32 test via wasmtime
        run: |
          WASM_BIN=$(find target/wasm32-wasip1/debug/deps -name 'determinism_golden_f32-*.wasm' -not -name '*.d' | head -1)
          echo "Executing: $WASM_BIN"
          wasmtime "$WASM_BIN"

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

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

      - uses: Swatinem/rust-cache@v2

      - name: Clippy (default features, lib + all targets)
        run: cargo clippy --all-targets -- -D warnings

      # `--all-features` is not possible: `wasm` × `ffi` is a deliberate
      # `compile_error!`, and `python` needs a Python toolchain. Every
      # other feature is exercised here so lint drift in feature-gated
      # code (parallel / neural / replay / ...) is a red gate, not a `-W`.
      - name: Clippy (full native feature set, lib + all targets)
        run: >-
          cargo clippy
          --features "std,simd,parallel,ffi,gpu-solver-bridge,neural,replay,analytics"
          --all-targets -- -D warnings

  # The declared `rust-version` is compiled with exactly that toolchain
  # (resolver = "3" makes the fresh resolution MSRV-aware, Cargo.lock is not
  # committed for this library) for the default, no_std and full native feature
  # sets. Bump `rust-version` and the
  # `@<version>` below in the same commit (karikari §3-1 #53 / template 罠 #27).
  # The pre-1.2.0 job regenerated the lockfile with stable and checked only the
  # std feature set, so the "1.70.0" claim was never actually compiled.
  msrv:
    name: MSRV (rust-version = 1.85)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: Install MSRV toolchain
        uses: dtolnay/rust-toolchain@1.85

      - uses: Swatinem/rust-cache@v2

      - name: cargo check on MSRV (lib, default features)
        run: cargo +1.85 check --lib
      - name: cargo check on MSRV (lib, no_std, rlib)
        run: cargo +1.85 rustc --lib --no-default-features --crate-type rlib
      # Library only: dev-dependencies (criterion / serde_derive) and the
      # sibling-crate bridge features (neural / replay / analytics) follow
      # their own MSRV, see README "MSRV Policy".
      - name: cargo check on MSRV (lib, core native feature set)
        run: >-
          cargo +1.85 check --lib
          --features "std,simd,parallel,ffi,gpu-solver-bridge"

  fmt:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

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

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

      # README § Determinism scope lists the f32 / f64 modules; the list is
      # generated from src/ and drifts silently when a module is added (1.2.0)
      - name: README f32 module row matches src/
        run: python3 scripts/f32_modules.py --check

  doc:
    name: Doc (RUSTDOCFLAGS=-Dwarnings)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

      # Both the default and the docs.rs feature set: feature-gated modules
      # (gpu_bridge / ffi) had 9 broken intra-doc links that the default-only
      # job never saw (1.2.0).
      - name: cargo doc (no-deps, default features)
        run: cargo doc --lib --no-deps
        env:
          RUSTDOCFLAGS: "-Dwarnings"
      - name: cargo doc (no-deps, docs.rs feature set)
        run: cargo doc --lib --no-deps --features "std,simd,parallel,ffi,gpu-solver-bridge"
        env:
          RUSTDOCFLAGS: "-Dwarnings"

  actionlint:
    name: actionlint (workflow YAML 検証)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: Run actionlint
        uses: reviewdog/action-actionlint@v1
        with:
          fail_level: error
          reporter: github-check

  feature-powerset:
    # 2026-09-15 追加 (karikari-review §13 / CI template 手順 4.6): feature 組合せの compile 網羅
    # depth 2 = 単体 + 2 個組合せ (19 通り、Mac 実測 green)
    # - wasm は `std` 必須 (`compile_error!`) なので powerset から外し、std,wasm を別 step で check
    # - python (pyo3 build) / neural / replay / analytics (sibling crate 実体必要) は除外
    name: Feature powerset (cargo-hack)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Create ALICE optional bridge stubs
        uses: ./.github/actions/alice-stubs
      - name: Install cargo-hack
        uses: taiki-e/install-action@cargo-hack
      # rustc --crate-type rlib: combos without `std` cannot build the cdylib
      # (no panic_handler / allocator), see "Build (no_std, rlib)" above.
      - name: Powerset (depth 2, wasm/python/bridge 除外)
        run: cargo hack rustc --lib --crate-type rlib --feature-powerset --depth 2 --exclude-features python,neural,replay,analytics,wasm
      - name: std + wasm (相互排他 ffi なし)
        run: cargo check --lib --no-default-features --features std,wasm