hf2q 0.1.3

Pure Rust CLI for converting HuggingFace models to hardware-optimized formats and serving them over an OpenAI-compatible API on Apple Silicon
name: CI

# Per-PR and per-push-to-main build + test gate.
#
# Runs on macOS because hf2q's inference path depends on mlx-native,
# which is Metal-only (Apple Silicon). The release-check.sh parity +
# perf gate is wired in a separate workflow (release-check.yml) that
# needs a self-hosted runner with the Gemma-4 26B DWQ GGUF.
#
# Lint strictness policy:
#   * cargo check + build --release + hosted-safe tests — BLOCKING.
#     GitHub's hosted macOS VM compiles Metal, but does not expose a usable
#     residency set. Real Metal tests require a self-hosted Apple Silicon gate.
#   * cargo clippy — INFORMATIONAL. The legacy tree has a broad lint backlog;
#     check/build/test remain the blocking correctness gates.
#   * cargo fmt --check — INFORMATIONAL (continue-on-error). The
#     baseline carries ~700 rustfmt diffs from earlier style drift;
#     running `cargo fmt --all` against the whole tree is a separate
#     policy decision (touches wide swaths of legacy code). Kept in
#     the log so new drift is visible.
#   * activate() warning-matrix smoke — BLOCKING; confirms the
#     HF2Q_UNSAFE_EXPERIMENTS ack gate (docs/shipping-contract.md)
#     still refuses/activates/warns correctly.

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

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

env:
  CARGO_TERM_COLOR: always

jobs:
  build-and-test:
    name: build + test (macos-latest)
    runs-on: macos-latest
    timeout-minutes: 30

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Rust toolchain (1.88.0, as pinned in Cargo.toml)
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: "1.88.0"
          components: rustfmt, clippy

      - name: Cache cargo registry + target
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
          restore-keys: cargo-${{ runner.os }}-

      # --- fmt: informational (baseline drift, see header) ---
      - name: cargo fmt --check (informational)
        continue-on-error: true
        run: cargo fmt --all -- --check

      # --- clippy: informational until the legacy lint backlog is retired ---
      - name: cargo clippy (informational)
        continue-on-error: true
        run: cargo clippy --locked --all-targets --all-features

      # --- Blocking compile + test ---
      - name: cargo check
        run: cargo check --locked --all-targets --all-features

      - name: cargo build --release
        run: cargo build --release --locked

      # GitHub's hosted macOS VM returns nil from Metal residency-set
      # creation. Keep the hosted gate blocking and deterministic by running
      # the library, native conversion, and non-Metal DeepSeek contracts here.
      # Metal execution is validated on real Apple Silicon; the declarative
      # parity/performance procedure lives in release-check.yml.
      - name: cargo test (hosted-safe contracts)
        run: |
          set -euo pipefail
          cargo test --locked --lib --all-features
          cargo test --locked --test convert_integration --all-features
          cargo test --locked --test lcp_registry_unit --all-features
          cargo test --locked --bin hf2q --all-features convert::orchestrator::tests::
          cargo test --locked --bin hf2q --all-features deepseek4 -- \
            --skip attention_forward_tests \
            --skip allocator_materializes_the_plan_as_zeroed_bf16_buffers \
            --skip cache_steps_publish_only_complete_groups_and_commit_transactionally \
            --skip partial_token_poison_requires_reset_before_replay \
            --skip start_zero_prefill_span_counts_complete_groups_and_publishes_once \
            --skip ffn_forward_tests \
            --skip raw_matmul_accepts_quality_sensitive_f32_weights \
            --skip embedding_forward_rejects_empty_input \
            --skip q2_k_embeddings_expand_to_four_identical_hc_streams \
            --skip native_model_load_keeps_weights_and_cache_on_one_device \
            --skip native_output_head_produces_finite_vocab_logits_and_rejects_shape_drift \
            --skip loader_preserves_raw_blocks_and_expands_only_elementwise_state \
            --skip loader_rejects_catalog_and_i32_storage_before_residency

      # --- Blocking: activate() matrix matches the shipping contract ---
      - name: activate() matrix smoke (clean / REFUSED / UNSAFE)
        shell: bash
        run: |
          set -euo pipefail

          echo "--- clean env should produce no investigation output ---"
          ./target/release/hf2q --help > /dev/null 2>/tmp/stderr_clean.log
          if [[ -s /tmp/stderr_clean.log ]]; then
            echo "FAIL: clean-env run produced stderr:" >&2
            cat /tmp/stderr_clean.log >&2
            exit 1
          fi
          echo "  OK"

          echo "--- HF2Q_F16_KV=1 without ack must REFUSE ---"
          HF2Q_F16_KV=1 ./target/release/hf2q --help \
              > /dev/null 2>/tmp/stderr_refused.log
          grep -q "REFUSED" /tmp/stderr_refused.log
          grep -q "HF2Q_F16_KV=1" /tmp/stderr_refused.log
          echo "  OK"

          echo "--- HF2Q_F16_KV=1 + HF2Q_UNSAFE_EXPERIMENTS=1 must ACTIVATE ---"
          HF2Q_F16_KV=1 HF2Q_UNSAFE_EXPERIMENTS=1 \
              ./target/release/hf2q --help \
              > /dev/null 2>/tmp/stderr_unsafe.log
          grep -q "UNSAFE (ack-required, activated)" /tmp/stderr_unsafe.log
          ! grep -q "REFUSED" /tmp/stderr_unsafe.log
          echo "  OK"