name: OpenClaw Cross-OS Release Checks (Reusable)

on:
  workflow_dispatch:
    inputs:
      ref:
        description: Public OpenClaw ref to validate (tag, branch, or full commit SHA)
        required: true
        default: main
        type: string
      workflow_ref:
        description: Optional openclaw/openclaw ref that provides the reusable workflow harness
        required: false
        default: ""
        type: string
      provider:
        description: Provider lane to use for onboarding and the end-to-end turn
        required: true
        default: openai
        type: choice
        options:
          - openai
          - anthropic
          - minimax
      mode:
        description: Which release-check lanes to run
        required: true
        default: both
        type: choice
        options:
          - fresh
          - upgrade
          - both
      suite_filter:
        description: Optional focused cross-OS suite filter, e.g. windows/packaged-upgrade or packaged-fresh
        required: false
        default: ""
        type: string
      previous_version:
        description: Optional baseline version for installer/dev-update and packaged upgrade
        required: false
        default: ""
        type: string
      ubuntu_runner:
        description: Optional Linux runner label override
        required: false
        default: ""
        type: string
      windows_runner:
        description: Optional Windows runner label override
        required: false
        default: ""
        type: string
      macos_runner:
        description: Optional macOS runner label override
        required: false
        default: ""
        type: string
      candidate_artifact_name:
        description: Optional current-run artifact name containing the candidate OpenClaw tarball
        required: false
        default: ""
        type: string
      candidate_artifact_run_id:
        description: Optional workflow run id for candidate_artifact_name
        required: false
        default: ""
        type: string
      candidate_file_name:
        description: Optional candidate tarball file name inside candidate_artifact_name
        required: false
        default: ""
        type: string
      candidate_version:
        description: Optional candidate OpenClaw package version
        required: false
        default: ""
        type: string
      candidate_source_sha:
        description: Optional source SHA used to build the candidate tarball
        required: false
        default: ""
        type: string
      openai_model:
        description: OpenAI model for release cross-OS agent-turn smoke
        required: false
        default: ""
        type: string
      advisory:
        description: Treat failures as advisory for the caller
        required: false
        default: false
        type: boolean
  workflow_call:
    inputs:
      advisory:
        description: Treat failures as advisory for the caller
        required: false
        default: false
        type: boolean
      ref:
        description: Public OpenClaw ref to validate (tag, branch, or full commit SHA)
        required: true
        type: string
      workflow_ref:
        description: Optional openclaw/openclaw ref that provides the reusable workflow harness
        required: false
        default: ""
        type: string
      provider:
        description: Provider lane to use for onboarding and the end-to-end turn
        required: true
        type: string
      mode:
        description: Which release-check lanes to run
        required: true
        type: string
      suite_filter:
        description: Optional focused cross-OS suite filter, e.g. windows/packaged-upgrade or packaged-fresh
        required: false
        default: ""
        type: string
      previous_version:
        description: Optional baseline version for the upgrade lane (defaults to npm latest)
        required: false
        default: ""
        type: string
      ubuntu_runner:
        description: Optional Linux runner label override
        required: false
        default: ""
        type: string
      windows_runner:
        description: Optional Windows runner label override
        required: false
        default: ""
        type: string
      macos_runner:
        description: Optional macOS runner label override
        required: false
        default: ""
        type: string
      candidate_artifact_name:
        description: Optional current-run artifact name containing the candidate OpenClaw tarball
        required: false
        default: ""
        type: string
      candidate_artifact_run_id:
        description: Optional workflow run id for candidate_artifact_name
        required: false
        default: ""
        type: string
      candidate_file_name:
        description: Optional candidate tarball file name inside candidate_artifact_name
        required: false
        default: ""
        type: string
      candidate_version:
        description: Optional candidate OpenClaw package version
        required: false
        default: ""
        type: string
      candidate_source_sha:
        description: Optional source SHA used to build the candidate tarball
        required: false
        default: ""
        type: string
      openai_model:
        description: OpenAI model for release cross-OS agent-turn smoke
        required: false
        default: ""
        type: string
    secrets:
      OPENAI_API_KEY:
        required: false
      ANTHROPIC_API_KEY:
        required: false
      MINIMAX_API_KEY:
        required: false
      OPENCLAW_DISCORD_SMOKE_BOT_TOKEN:
        required: false
      OPENCLAW_DISCORD_SMOKE_GUILD_ID:
        required: false
      OPENCLAW_DISCORD_SMOKE_CHANNEL_ID:
        required: false

permissions: read-all

concurrency:
  group: openclaw-cross-os-release-checks-${{ inputs.ref }}-${{ inputs.provider }}-${{ inputs.mode }}
  cancel-in-progress: ${{ inputs.ref == 'main' }}

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
  NODE_VERSION: "24.15.0"
  OPENCLAW_REPOSITORY: openclaw/openclaw
  TSX_VERSION: "4.21.0"
  OPENCLAW_CROSS_OS_OPENAI_MODEL: ${{ inputs.openai_model || vars.OPENCLAW_CROSS_OS_OPENAI_MODEL || 'openai/gpt-5.5' }}

jobs:
  prepare:
    runs-on: ubuntu-24.04
    continue-on-error: ${{ inputs.advisory }}
    outputs:
      baseline_file_name: ${{ steps.baseline_metadata.outputs.file_name }}
      baseline_spec: ${{ steps.baseline.outputs.value }}
      candidate_file_name: ${{ steps.candidate_metadata.outputs.file_name }}
      candidate_version: ${{ steps.candidate_metadata.outputs.version }}
      matrix: ${{ steps.matrix.outputs.value }}
      source_sha: ${{ steps.candidate_metadata.outputs.source_sha }}
      workflow_ref: ${{ steps.workflow_ref.outputs.value }}
    steps:
      - name: Validate provider secret availability
        env:
          PROVIDER: ${{ inputs.provider }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
        run: |
          set -euo pipefail
          case "${PROVIDER}" in
            openai)
              [[ -n "${OPENAI_API_KEY}" ]] || { echo "Missing OPENAI_API_KEY secret." >&2; exit 1; }
              ;;
            anthropic)
              [[ -n "${ANTHROPIC_API_KEY}" ]] || { echo "Missing ANTHROPIC_API_KEY secret." >&2; exit 1; }
              ;;
            minimax)
              [[ -n "${MINIMAX_API_KEY}" ]] || { echo "Missing MINIMAX_API_KEY secret." >&2; exit 1; }
              ;;
            *)
              echo "Unsupported provider: ${PROVIDER}" >&2
              exit 1
              ;;
          esac

      - name: Resolve workflow ref
        id: workflow_ref
        env:
          INPUT_WORKFLOW_REF: ${{ inputs.workflow_ref }}
          CALLER_REPOSITORY: ${{ github.repository }}
          CURRENT_SHA: ${{ github.sha }}
          WORKFLOW_CONTEXT_REF: ${{ github.workflow_ref }}
          WORKFLOW_REPOSITORY: ${{ env.OPENCLAW_REPOSITORY }}
        run: |
          set -euo pipefail
          resolve_unique_remote_ref() {
            local remote_url="$1"
            shift
            local -a refs=("$@")
            local -a matches=()
            local ref=""

            for ref in "${refs[@]}"; do
              [[ -n "${ref}" ]] || continue
              mapfile -t matches < <(
                git ls-remote "${remote_url}" "${ref}" | awk '{print $1}' | awk '!seen[$0]++'
              )
              if [[ "${#matches[@]}" -eq 0 ]]; then
                continue
              fi
              if [[ "${#matches[@]}" -ne 1 ]]; then
                return 2
              fi

              printf '%s\n' "${matches[0]}"
              return 0
            done
            return 1
          }

          if [[ -n "${INPUT_WORKFLOW_REF}" ]]; then
            TARGET_REF="${INPUT_WORKFLOW_REF}"
          elif [[ "${CALLER_REPOSITORY}" == "${WORKFLOW_REPOSITORY}" ]]; then
            TARGET_REF="${CURRENT_SHA}"
          elif [[ "${WORKFLOW_CONTEXT_REF}" == "${WORKFLOW_REPOSITORY}/"* ]] && [[ "${WORKFLOW_CONTEXT_REF}" == *"@"* ]]; then
            TARGET_REF="${WORKFLOW_CONTEXT_REF##*@}"
          else
            echo "Failed to infer workflow ref from github.workflow_ref=${WORKFLOW_CONTEXT_REF}" >&2
            exit 1
          fi

          if [[ "${TARGET_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then
            echo "value=${TARGET_REF}" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          REMOTE_URL="https://github.com/${WORKFLOW_REPOSITORY}.git"
          if [[ "${TARGET_REF}" == refs/* ]]; then
            if [[ "${TARGET_REF}" == refs/tags/* ]]; then
              mapfile -t MATCHES < <(
                resolve_unique_remote_ref "${REMOTE_URL}" "${TARGET_REF}^{}" "${TARGET_REF}" || true
              )
            else
              mapfile -t MATCHES < <(resolve_unique_remote_ref "${REMOTE_URL}" "${TARGET_REF}" || true)
            fi
          else
            mapfile -t BRANCH_MATCHES < <(
              resolve_unique_remote_ref "${REMOTE_URL}" "refs/heads/${TARGET_REF}" || true
            )
            mapfile -t TAG_MATCHES < <(
              resolve_unique_remote_ref "${REMOTE_URL}" "refs/tags/${TARGET_REF}^{}" "refs/tags/${TARGET_REF}" || true
            )

            MATCH_COUNT=$(( ${#BRANCH_MATCHES[@]} + ${#TAG_MATCHES[@]} ))
            if [[ "${MATCH_COUNT}" -eq 1 ]]; then
              if [[ "${#BRANCH_MATCHES[@]}" -eq 1 ]]; then
                MATCHES=("${BRANCH_MATCHES[0]}")
              else
                MATCHES=("${TAG_MATCHES[0]}")
              fi
            elif [[ "${MATCH_COUNT}" -eq 0 ]]; then
              MATCHES=()
            else
              echo "Workflow ref resolved ambiguously: ${TARGET_REF}" >&2
              exit 1
            fi
          fi

          case "${#MATCHES[@]}" in
            1)
              echo "value=${MATCHES[0]}" >> "$GITHUB_OUTPUT"
              ;;
            0)
              echo "Failed to resolve workflow ref: ${TARGET_REF}" >&2
              exit 1
              ;;
            *)
              echo "Workflow ref resolved ambiguously: ${TARGET_REF}" >&2
              exit 1
              ;;
          esac

      - name: Checkout workflow repo
        uses: actions/checkout@v6
        with:
          repository: ${{ env.OPENCLAW_REPOSITORY }}
          ref: ${{ steps.workflow_ref.outputs.value }}
          path: workflow
          fetch-depth: 1
          persist-credentials: true

      - name: Checkout public source ref
        if: inputs.candidate_artifact_name == ''
        uses: actions/checkout@v6
        with:
          repository: ${{ env.OPENCLAW_REPOSITORY }}
          ref: ${{ inputs.ref }}
          path: source
          fetch-depth: 0
          persist-credentials: true
          submodules: recursive

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Setup pnpm
        uses: ./workflow/.github/actions/setup-pnpm-store-cache
        with:
          node-version: ${{ env.NODE_VERSION }}
          package-manager-file: ${{ inputs.candidate_artifact_name == '' && 'source/package.json' || 'workflow/package.json' }}
          lockfile-path: ${{ inputs.candidate_artifact_name == '' && 'source/pnpm-lock.yaml' || 'workflow/pnpm-lock.yaml' }}
          use-actions-cache: ${{ inputs.candidate_artifact_name == '' && 'true' || 'false' }}

      - name: Ensure pnpm store cache directory exists
        run: mkdir -p "$(pnpm store path --silent)"

      - name: Build candidate artifact once
        if: inputs.candidate_artifact_name == ''
        env:
          OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare
        run: |
          bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh \
            --prepare-only \
            --source-dir source \
            --output-dir "${OUTPUT_DIR}"

      - name: Download current-run candidate artifact
        if: inputs.candidate_artifact_name != '' && inputs.candidate_artifact_run_id == ''
        uses: actions/download-artifact@v8
        with:
          name: ${{ inputs.candidate_artifact_name }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package

      - name: Download previous-run candidate artifact
        if: inputs.candidate_artifact_name != '' && inputs.candidate_artifact_run_id != ''
        uses: actions/download-artifact@v8
        with:
          name: ${{ inputs.candidate_artifact_name }}
          run-id: ${{ inputs.candidate_artifact_run_id }}
          github-token: ${{ github.token }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package

      - name: Capture provided candidate artifact metadata
        if: inputs.candidate_artifact_name != ''
        env:
          PACKAGE_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package
          INPUT_CANDIDATE_FILE_NAME: ${{ inputs.candidate_file_name }}
          INPUT_CANDIDATE_VERSION: ${{ inputs.candidate_version }}
          INPUT_CANDIDATE_SOURCE_SHA: ${{ inputs.candidate_source_sha }}
          CANDIDATE_JSON: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/candidate.json
        run: |
          node <<'NODE'
          const fs = require("node:fs");
          const path = require("node:path");

          const packageDir = process.env.PACKAGE_DIR;
          const requestedFileName = process.env.INPUT_CANDIDATE_FILE_NAME.trim();
          const files = fs.readdirSync(packageDir).filter((file) => file.endsWith(".tgz"));
          const candidateFileName = requestedFileName || (files.length === 1 ? files[0] : "");
          if (!candidateFileName) {
            throw new Error(`Expected exactly one candidate .tgz in ${packageDir}; found ${files.length}.`);
          }
          if (!fs.existsSync(path.join(packageDir, candidateFileName))) {
            throw new Error(`Provided candidate artifact does not contain ${candidateFileName}.`);
          }
          const candidateVersion = process.env.INPUT_CANDIDATE_VERSION.trim();
          if (!candidateVersion) {
            throw new Error("candidate_version is required when candidate_artifact_name is provided.");
          }
          const sourceSha = process.env.INPUT_CANDIDATE_SOURCE_SHA.trim();
          if (!/^[0-9a-f]{40}$/iu.test(sourceSha)) {
            throw new Error("candidate_source_sha must be a full commit SHA when candidate_artifact_name is provided.");
          }
          fs.writeFileSync(
            process.env.CANDIDATE_JSON,
            `${JSON.stringify({ candidateFileName, candidateVersion, sourceSha }, null, 2)}\n`,
          );
          NODE

      - name: Resolve baseline package spec
        if: ${{ inputs.mode != 'fresh' }}
        id: baseline
        env:
          INPUT_PREVIOUS_VERSION: ${{ inputs.previous_version }}
        run: |
          set -euo pipefail
          if [[ -n "${INPUT_PREVIOUS_VERSION}" ]]; then
            echo "value=openclaw@${INPUT_PREVIOUS_VERSION}" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          BASELINE_VERSION="$(npm view openclaw@latest version)"
          echo "value=openclaw@${BASELINE_VERSION}" >> "$GITHUB_OUTPUT"

      - name: Pack baseline artifact
        if: ${{ inputs.mode != 'fresh' }}
        env:
          BASELINE_SPEC: ${{ steps.baseline.outputs.value }}
          OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/baseline
        run: |
          mkdir -p "${OUTPUT_DIR}"
          timeout --preserve-status 300s npm pack --ignore-scripts --json "${BASELINE_SPEC}" --pack-destination "${OUTPUT_DIR}" > "${OUTPUT_DIR}/pack.json"

      - name: Capture candidate metadata
        id: candidate_metadata
        env:
          CANDIDATE_JSON: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/candidate.json
        run: |
          node <<'NODE' >>"$GITHUB_OUTPUT"
          const fs = require("node:fs");
          const payload = JSON.parse(fs.readFileSync(process.env.CANDIDATE_JSON, "utf8"));
          process.stdout.write(`file_name=${payload.candidateFileName}\n`);
          process.stdout.write(`version=${payload.candidateVersion}\n`);
          process.stdout.write(`source_sha=${payload.sourceSha}\n`);
          NODE

      - name: Capture baseline metadata
        if: ${{ inputs.mode != 'fresh' }}
        id: baseline_metadata
        env:
          BASELINE_PACK_JSON: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/baseline/pack.json
        run: |
          node <<'NODE' >>"$GITHUB_OUTPUT"
          const fs = require("node:fs");
          const payload = JSON.parse(fs.readFileSync(process.env.BASELINE_PACK_JSON, "utf8"));
          const entry = Array.isArray(payload) ? payload.at(-1) : null;
          if (!entry?.filename) {
            throw new Error("Baseline npm pack did not produce a filename.");
          }
          process.stdout.write(`file_name=${entry.filename}\n`);
          NODE

      - name: Upload candidate artifact
        uses: actions/upload-artifact@v7
        with:
          name: openclaw-cross-os-release-checks-candidate-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package/${{ steps.candidate_metadata.outputs.file_name }}
          if-no-files-found: error

      - name: Upload baseline artifact
        if: ${{ inputs.mode != 'fresh' }}
        uses: actions/upload-artifact@v7
        with:
          name: openclaw-cross-os-release-checks-baseline-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/baseline/${{ steps.baseline_metadata.outputs.file_name }}
          if-no-files-found: error

      - name: Resolve runner matrix
        id: matrix
        env:
          INPUT_REF: ${{ inputs.ref }}
          INPUT_MODE: ${{ inputs.mode }}
          INPUT_SUITE_FILTER: ${{ inputs.suite_filter }}
          INPUT_UBUNTU_RUNNER: ${{ inputs.ubuntu_runner }}
          INPUT_WINDOWS_RUNNER: ${{ inputs.windows_runner }}
          INPUT_MACOS_RUNNER: ${{ inputs.macos_runner }}
          VAR_UBUNTU_RUNNER: ${{ vars.OPENCLAW_RELEASE_CHECKS_UBUNTU_RUNNER }}
          VAR_WINDOWS_RUNNER: ${{ vars.OPENCLAW_RELEASE_CHECKS_WINDOWS_RUNNER }}
          VAR_MACOS_RUNNER: ${{ vars.OPENCLAW_RELEASE_CHECKS_MACOS_RUNNER }}
        run: |
          MATRIX_JSON="$(bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh \
            --resolve-matrix \
            --ref "${INPUT_REF}" \
            --mode "${INPUT_MODE}" \
            --suite-filter "${INPUT_SUITE_FILTER}" \
            --ubuntu-runner "${INPUT_UBUNTU_RUNNER}" \
            --windows-runner "${INPUT_WINDOWS_RUNNER}" \
            --macos-runner "${INPUT_MACOS_RUNNER}")"
          echo "value=${MATRIX_JSON}" >> "$GITHUB_OUTPUT"

  cross_os_release_checks:
    name: "${{ matrix.display_name }} / ${{ matrix.suite_label }}"
    needs: prepare
    continue-on-error: ${{ inputs.advisory }}
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
    runs-on: ${{ matrix.runner }}
    timeout-minutes: 60
    steps:
      - name: Checkout workflow repo
        uses: actions/checkout@v6
        with:
          repository: ${{ env.OPENCLAW_REPOSITORY }}
          ref: ${{ needs.prepare.outputs.workflow_ref }}
          path: workflow
          fetch-depth: 1
          persist-credentials: true

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Setup pnpm
        uses: ./workflow/.github/actions/setup-pnpm-store-cache
        with:
          node-version: ${{ env.NODE_VERSION }}
          package-manager-file: workflow/package.json
          lockfile-path: workflow/pnpm-lock.yaml
          use-actions-cache: "false"

      - name: Download candidate artifact
        id: download_candidate
        continue-on-error: true
        uses: actions/download-artifact@v8
        with:
          name: openclaw-cross-os-release-checks-candidate-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/candidate

      - name: Retry candidate artifact download
        if: ${{ steps.download_candidate.outcome == 'failure' }}
        uses: actions/download-artifact@v8
        with:
          name: openclaw-cross-os-release-checks-candidate-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/candidate

      - name: Download baseline artifact
        if: ${{ matrix.suite == 'packaged-upgrade' }}
        id: download_baseline
        continue-on-error: true
        uses: actions/download-artifact@v8
        with:
          name: openclaw-cross-os-release-checks-baseline-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/baseline

      - name: Retry baseline artifact download
        if: ${{ matrix.suite == 'packaged-upgrade' && steps.download_baseline.outcome == 'failure' }}
        uses: actions/download-artifact@v8
        with:
          name: openclaw-cross-os-release-checks-baseline-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/baseline

      - name: Verify release-check inputs
        shell: bash
        env:
          CANDIDATE_TGZ: ${{ runner.temp }}/openclaw-cross-os-release-checks/candidate/${{ needs.prepare.outputs.candidate_file_name }}
          BASELINE_TGZ: ${{ runner.temp }}/openclaw-cross-os-release-checks/baseline/${{ needs.prepare.outputs.baseline_file_name }}
          OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/${{ matrix.artifact_name }}-${{ matrix.suite }}
          SUITE: ${{ matrix.suite }}
        run: |
          mkdir -p "${OUTPUT_DIR}"
          if [[ ! -f "${CANDIDATE_TGZ}" ]]; then
            echo "::error::candidate artifact missing: ${CANDIDATE_TGZ}"
            exit 1
          fi
          if [[ "${SUITE}" == "packaged-upgrade" ]] && [[ ! -f "${BASELINE_TGZ}" ]]; then
            echo "::error::baseline artifact missing: ${BASELINE_TGZ}"
            exit 1
          fi

      - name: Run cross-OS release checks
        shell: bash
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
          OPENCLAW_DISCORD_SMOKE_BOT_TOKEN: ${{ secrets.OPENCLAW_DISCORD_SMOKE_BOT_TOKEN }}
          OPENCLAW_DISCORD_SMOKE_GUILD_ID: ${{ secrets.OPENCLAW_DISCORD_SMOKE_GUILD_ID }}
          OPENCLAW_DISCORD_SMOKE_CHANNEL_ID: ${{ secrets.OPENCLAW_DISCORD_SMOKE_CHANNEL_ID }}
          OPENCLAW_RELEASE_CHECK_OS: ${{ matrix.os_id }}
          OPENCLAW_RELEASE_CHECK_RUNNER: ${{ matrix.runner }}
          CANDIDATE_TGZ: ${{ runner.temp }}/openclaw-cross-os-release-checks/candidate/${{ needs.prepare.outputs.candidate_file_name }}
          CANDIDATE_VERSION: ${{ needs.prepare.outputs.candidate_version }}
          SOURCE_SHA: ${{ needs.prepare.outputs.source_sha }}
          BASELINE_SPEC: ${{ needs.prepare.outputs.baseline_spec }}
          PREVIOUS_VERSION: ${{ inputs.previous_version }}
          BASELINE_TGZ: ${{ runner.temp }}/openclaw-cross-os-release-checks/baseline/${{ needs.prepare.outputs.baseline_file_name }}
          PROVIDER: ${{ inputs.provider }}
          MODE: ${{ matrix.lane }}
          SUITE: ${{ matrix.suite }}
          REF: ${{ inputs.ref }}
          OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/${{ matrix.artifact_name }}-${{ matrix.suite }}
        run: |
          DISCORD_ARGS=()
          if [[ -n "${OPENCLAW_DISCORD_SMOKE_BOT_TOKEN}" ]] && [[ -n "${OPENCLAW_DISCORD_SMOKE_GUILD_ID}" ]] && [[ -n "${OPENCLAW_DISCORD_SMOKE_CHANNEL_ID}" ]]; then
            DISCORD_ARGS+=(--run-discord-roundtrip true)
          fi
          bash workflow/scripts/github/run-openclaw-cross-os-release-checks.sh \
            --candidate-tgz "${CANDIDATE_TGZ}" \
            --candidate-version "${CANDIDATE_VERSION}" \
            --source-sha "${SOURCE_SHA}" \
            --baseline-spec "${BASELINE_SPEC}" \
            --previous-version "${PREVIOUS_VERSION}" \
            --baseline-tgz "${BASELINE_TGZ}" \
            --provider "${PROVIDER}" \
            --mode "${MODE}" \
            --suite "${SUITE}" \
            --ref "${REF}" \
            "${DISCORD_ARGS[@]}" \
            --output-dir "${OUTPUT_DIR}"

      - name: Summarize release checks
        if: always()
        shell: bash
        env:
          SUMMARY_PATH: ${{ runner.temp }}/openclaw-cross-os-release-checks/${{ matrix.artifact_name }}-${{ matrix.suite }}/summary.md
        run: |
          if [[ -f "${SUMMARY_PATH}" ]]; then
            cat "${SUMMARY_PATH}" >> "$GITHUB_STEP_SUMMARY"
          else
            mkdir -p "$(dirname "${SUMMARY_PATH}")"
            echo "No summary generated." | tee "${SUMMARY_PATH}" >> "$GITHUB_STEP_SUMMARY"
          fi

      - name: Upload release-check artifacts
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: openclaw-cross-os-release-checks-${{ matrix.artifact_name }}-${{ matrix.suite }}-${{ github.run_id }}
          path: ${{ runner.temp }}/openclaw-cross-os-release-checks/${{ matrix.artifact_name }}-${{ matrix.suite }}
          if-no-files-found: error
