name: Rust check (native)

on:
  pull_request:
    paths:
      - 'Cargo.toml'
      - 'Cargo.lock'
      - 'crates/**'
      - 'rust-toolchain.toml'
      - 'rustfmt.toml'
      - 'deny.toml'
      - 'tools/check-jian-boundaries.sh'
      - 'tools/check-widget-boundary.sh'
      - 'tools/dump-diagnostics-golden.ts'
      - 'packages/pen-ai-skills/**'
      - '.github/workflows/rust-check.yml'
      # TS planner-prompt source — drives the parity drift guard.
      - 'apps/web/src/services/ai/orchestrator-prompt-optimizer.ts'
  push:
    branches: ['**']
    paths:
      - 'Cargo.toml'
      - 'Cargo.lock'
      - 'crates/**'
      - 'vendor/**'
      - 'deny.toml'
      - 'rust-toolchain.toml'
      - 'rustfmt.toml'
      - 'tools/check-jian-boundaries.sh'
      - 'tools/check-widget-boundary.sh'
      - 'tools/dump-diagnostics-golden.ts'
      - 'packages/pen-ai-skills/**'
      - '.github/workflows/rust-check.yml'
      # TS planner-prompt source — drives the parity drift guard.
      - 'apps/web/src/services/ai/orchestrator-prompt-optimizer.ts'

jobs:
  check:
    name: ${{ matrix.os }} / 1.94
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-latest, ubuntu-latest, windows-latest]
    steps:
      # Keep LF line endings on the Windows runner so `cargo fmt --check`
      # does not trip over CRLF-converted vendored sources.
      - name: Disable autocrlf
        run: git config --global core.autocrlf false
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: '1.94'
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
      # Linux GL prerequisites for winit's x11+wayland feature link.
      # libxkbcommon-x11-dev / libwayland-dev are winit's link-time deps on
      # Linux; the wayland-csd-adwaita feature pulls in xkbcommon + wayland-client.
      - name: Install Linux GL prereqs
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            libxkbcommon-dev libxkbcommon-x11-dev \
            libwayland-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
            libegl1-mesa-dev libgles2-mesa-dev libgbm-dev mesa-utils \
            libfreetype-dev libfontconfig1-dev
      - run: cargo fmt --all -- --check
      - run: cargo build --workspace
      # GitHub-hosted runners are headless with no reliable Mesa software
      # EGL, so STEP1A_REQUIRE_GPU is left unset: the op-host-native GPU
      # smoke (memory_loop) self-uses the raster fallback instead of
      # hard-failing on EGL init. Real-GPU coverage runs on dev machines.
      - run: cargo test --workspace
      - run: cargo clippy --workspace --all-targets -- -D warnings
      # Step 1a Phase C Task 4: spec v19 §11 + §12.3 boundary invariants.
      # Linux runner has the full mobile target stdlib (`rustup target add`
      # in subsequent steps would handle ios/android cargo metadata too,
      # but the script uses `cargo tree --target` which only needs the
      # cfg-gate evaluation, not the actual target sysroot).
      - name: Verify Jian boundary invariants
        if: runner.os == 'Linux'
        run: |
          rustup target add aarch64-linux-android aarch64-apple-ios wasm32-unknown-unknown
          bash tools/check-jian-boundaries.sh

      - name: Verify Step 1b widget boundary (spec §1.4)
        if: runner.os == 'Linux'
        run: bash tools/check-widget-boundary.sh

      - name: Install Bun
        if: runner.os == 'Linux'
        uses: oven-sh/setup-bun@v2

      - name: Install JS deps
        if: runner.os == 'Linux'
        run: bun install --frozen-lockfile

      - name: Planner-prompt parity drift guard
        if: runner.os == 'Linux'
        run: |
          bun run crates/op-orchestrator/tools/dump-planner-golden.ts
          git diff --exit-code crates/op-orchestrator/tests/planner-golden/ \
            || (echo "::error::planner golden drifted — rerun dump-planner-golden.ts" && exit 1)

  deny:
    name: cargo-deny (native)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: dtolnay/rust-toolchain@stable
      - uses: EmbarkStudios/cargo-deny-action@v2 # auto-updates within v2.x; runner cargo at this stage carries cargo-deny 0.18+ which handles modern transitive manifests (Phase 1 Task 1.8 finding)
        with:
          command: check

  # S1 Plan C Task 6 — golden parity drift guard (spec §8, Risk R4).
  # The TS `pen-ai-skills` diagnostics layer is the parity oracle for the
  # Rust `op-design-lint` detectors. This job re-runs the golden-dump script
  # against the live TS detectors and fails if the regenerated golden differs
  # from the committed copy — catching a TS detector change that would
  # silently stale the Rust parity baseline (`tests/parity.rs`).
  diagnostics-golden-drift:
    name: diagnostics golden drift
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: oven-sh/setup-bun@v2
      - name: Install workspace dependencies
        run: bun install --frozen-lockfile
      - name: Regenerate diagnostics golden from the TS oracle
        run: bun run tools/dump-diagnostics-golden.ts
      - name: Fail on golden drift
        # A non-empty diff means the committed golden no longer matches the
        # TS detectors — regenerate locally (`bun run tools/dump-diagnostics-golden.ts`)
        # and re-verify the Rust parity test before committing.
        run: git diff --exit-code crates/op-design-lint/tests/fixtures/golden/
