alice-physics 2.2.0

Deterministic physics engine: 128-bit fixed-point rigid bodies with bit-exact, replayable results, typed observation and rollback for search and planning, plus FEM, CFD and 3D-print analysis modules
Documentation
name: Fuzz

# ALICE-SDF v1.7.7 (commit 52d26f9) を base に ALICE-Physics 用に調整
#
# 目的:
# - fuzz/Cargo.toml の [[bin]] を全部 build し (push / PR、build-all job)、push / PR では
#   core 4 target、夜間と手動では全 target を time-boxed 実行 (一覧は scripts/fuzz_targets.py)
# - 決定論 lockstep が scene format 経由で保たれることを検証 (roundtrip)
#
# 罠:
# - cargo-fuzz は nightly toolchain 必須 (rust-toolchain 不使用時も明示指定)

on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'fuzz/**'
      - 'scripts/fuzz_targets.py'
      - '.github/workflows/fuzz.yml'
      - '.github/actions/**'
  pull_request:
    branches: [main]
    paths:
      - 'src/**'
      - 'fuzz/**'
      - 'scripts/fuzz_targets.py'
      - '.github/workflows/fuzz.yml'
      - '.github/actions/**'
  schedule:
    # 毎日 03:00 UTC = 12:00 JST (idle 時間帯)
    - cron: '0 3 * * *'
  workflow_dispatch:
    inputs:
      duration_seconds:
        description: 'Per-target fuzz duration (seconds)'
        required: false
        default: '60'
        type: string

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

env:
  CARGO_TERM_COLOR: always

jobs:
  # the targets to run, from fuzz/Cargo.toml: the core set on push / PR, every
  # [[bin]] on the nightly schedule and on a manual run
  targets:
    name: Fuzz targets
    runs-on: ubuntu-latest
    outputs:
      list: ${{ steps.list.outputs.list }}
    steps:
      - uses: actions/checkout@v5
      - name: List the fuzz targets
        id: list
        run: |
          python3 scripts/test_fuzz_targets.py
          list=$(python3 scripts/fuzz_targets.py --matrix "${{ github.event_name }}")
          echo "list=$list" >> "$GITHUB_OUTPUT"
          echo "targets: $list"

  # every target compiles on every push and PR, including those only run nightly
  build-all:
    name: Fuzz — build every target
    if: github.event_name == 'push' || github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: Install nightly toolchain
        uses: dtolnay/rust-toolchain@nightly
      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: fuzz
      - name: Build every [[bin]] of fuzz/Cargo.toml
        run: |
          cd fuzz
          cargo +nightly build --bins

  fuzz:
    name: Fuzz — ${{ matrix.target }}
    needs: targets
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        target: ${{ fromJSON(needs.targets.outputs.list) }}
    steps:
      - uses: actions/checkout@v5

      # cargo-fuzz は nightly toolchain 必須
      - name: Install nightly toolchain
        uses: dtolnay/rust-toolchain@nightly

      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: fuzz

      - name: Install cargo-fuzz
        run: cargo install cargo-fuzz --locked

      # build は blocking: compile error を continue-on-error で隠さない
      # (2026-09-15 まで job-level continue-on-error だったため
      #  fuzz_deterministic_roundtrip の E0639 が 2 日間 green 表示で silent だった)
      - name: Build fuzz target
        run: |
          cd fuzz
          cargo +nightly fuzz build "${{ matrix.target }}"

      - name: Set fuzz duration
        id: dur
        run: |
          DURATION="${{ github.event.inputs.duration_seconds }}"
          if [ -z "$DURATION" ]; then
            DURATION=60
          fi
          echo "seconds=$DURATION" >> "$GITHUB_OUTPUT"

      # a crash is a reproducible defect: the job fails, and the input that
      # crashed is kept as an artifact for triage
      - name: Run fuzz target (time-boxed)
        id: run
        run: |
          cd fuzz
          cargo +nightly fuzz run "${{ matrix.target }}" -- \
            -max_total_time=${{ steps.dur.outputs.seconds }}

      - name: Upload crash artifacts (if any)
        if: failure() && steps.run.outcome == 'failure'
        uses: actions/upload-artifact@v6
        with:
          name: fuzz-crash-${{ matrix.target }}
          path: fuzz/artifacts/${{ matrix.target }}
          if-no-files-found: ignore
          retention-days: 14

      - name: Report crash
        if: failure() && steps.run.outcome == 'failure'
        run: echo "::error::fuzz target ${{ matrix.target }} found a crash — see the fuzz-crash-${{ matrix.target }} artifact"