name: Intelligence experiments
on:
  workflow_dispatch:
    inputs:
      experiment:
        description: Committed experiment design (each dispatch is a new attempt)
        type: choice
        required: true
        default: preflight
        options: [context_control, preflight, native, preview, restart, broad]

permissions:
  contents: read

# Never cancel a scored attempt or compete with normal intelligence CI's lock.
concurrency:
  group: intelligence-experiments-${{ github.ref }}
  cancel-in-progress: false

jobs:
  experiment:
    runs-on: ubuntu-latest
    timeout-minutes: 40
    env:
      QUALITY_ALLOW_DOWNLOADS: '1'
      ATTOCODE_LOCAL_ONLY: '1'
      HF_HUB_OFFLINE: '1'
      TRANSFORMERS_OFFLINE: '1'
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v7
        with:
          python-version: '3.12'
      - name: Install maintained test dependencies (outside payload budget)
        timeout-minutes: 5
        run: uv sync --locked --package attocode-code-intel --extra dev
      - name: Install context HTTP control dependencies (outside payload budget)
        if: inputs.experiment == 'context_control'
        timeout-minutes: 5
        run: uv sync --locked --package attocode-code-intel --extra dev --extra service
      - name: Acquire native diagnostic tools (outside payload budget)
        if: inputs.experiment == 'native'
        timeout-minutes: 5
        env:
          QUALITY_GRAMMAR_CACHE: ${{ runner.temp }}/native-grammars-${{ github.run_id }}-${{ github.run_attempt }}
        run: |
          exec > >(tee '${{ runner.temp }}/native-setup-${{ github.run_id }}-${{ github.run_attempt }}.log') 2>&1
          uv sync --locked --package attocode-code-intel --extra dev --extra tree-sitter-all
          rustup toolchain install 1.94.1 --profile minimal --component rust-analyzer --component rust-src
          rustup default 1.94.1
          uv run --no-sync python - <<'PY'
          import os
          from pathlib import Path
          import tree_sitter_language_pack as pack
          cache = Path(os.environ['QUALITY_GRAMMAR_CACHE'])
          cache.mkdir()
          pack.configure(cache_dir=str(cache))
          pack.download(['ocaml'])
          assert 'ocaml' in pack.downloaded_languages()
          PY
      - name: Acquire broad fixture history and grammars (outside payload budget)
        if: inputs.experiment == 'broad'
        timeout-minutes: 5
        env:
          QUALITY_GRAMMAR_CACHE: ${{ runner.temp }}/native-grammars-${{ github.run_id }}-${{ github.run_attempt }}
        run: |
          exec > >(tee '${{ runner.temp }}/broad-setup-${{ github.run_id }}-${{ github.run_attempt }}.log') 2>&1
          uv sync --locked --package attocode-code-intel --extra dev --extra tree-sitter-all
          uv run --no-sync python - <<'PYSETUP'
          import json, os, re, subprocess
          from pathlib import Path
          import yaml
          import tree_sitter_language_pack as pack
          frontend = next(s for s in yaml.safe_load(Path('eval/quality/workflows.yaml').read_text())['repositories'] if s['id'] == 'frontend')
          assert frontend['repo'] == 'eren23/attocode' and re.fullmatch('[0-9a-f]{40}', frontend['commit'])
          subprocess.run(['git', 'fetch', '--no-tags', '--depth', '1', '--filter=blob:limit=1000001', 'origin', frontend['commit']], check=True)
          subprocess.run(['git', 'cat-file', '-e', frontend['commit'] + '^{commit}'], check=True)
          cache = Path(os.environ['QUALITY_GRAMMAR_CACHE'])
          cache.mkdir()
          pack.configure(cache_dir=str(cache))
          names = {'haskell', 'erlang', 'clojure', 'perl', 'dart', 'ocaml', 'fsharp', 'julia', 'nim', 'r', 'objc', 'crystal'}
          available = set(pack.manifest_languages())
          assert not (names - available - {'crystal'})
          requested = sorted(names & available)
          pack.download(requested)
          assert set(requested) <= set(pack.downloaded_languages())
          print(json.dumps({'requested': requested, 'unsupported': sorted(names - available)}))
          PYSETUP
      - name: Require broad platform controls before compatibility
        if: inputs.experiment == 'broad'
        timeout-minutes: 5
        run: |
          uv run --no-sync python -c "import sys, platform; assert sys.platform == 'linux' and platform.machine() == 'x86_64'"
          uv run --no-sync python -m eval.quality.experiments preflight --root '${{ runner.temp }}/broad-platform-controls-${{ github.run_id }}-${{ github.run_attempt }}'
      - uses: actions/upload-artifact@v4
        if: always() && inputs.experiment == 'broad'
        with:
          name: experiment-broad-platform-preflight-${{ github.run_id }}-${{ github.run_attempt }}
          retention-days: 14
          if-no-files-found: error
          path: ${{ runner.temp }}/broad-platform-controls-${{ github.run_id }}-${{ github.run_attempt }}/evidence/
      - name: Run committed design under aggregate limits
        env:
          EXPERIMENT: ${{ inputs.experiment }}
          QUALITY_EXPERIMENT_ROOT: ${{ runner.temp }}/intelligence-experiment-${{ github.run_id }}-${{ github.run_attempt }}
          QUALITY_GRAMMAR_CACHE: ${{ runner.temp }}/native-grammars-${{ github.run_id }}-${{ github.run_attempt }}
        run: uv run --no-sync python -m eval.quality.experiments "$EXPERIMENT" --root "$QUALITY_EXPERIMENT_ROOT"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: experiment-${{ inputs.experiment }}-${{ github.run_id }}-${{ github.run_attempt }}
          retention-days: 14
          if-no-files-found: error
          path: |
            ${{ runner.temp }}/intelligence-experiment-${{ github.run_id }}-${{ github.run_attempt }}/evidence/
            ${{ runner.temp }}/native-setup-${{ github.run_id }}-${{ github.run_attempt }}.log
            ${{ runner.temp }}/broad-setup-${{ github.run_id }}-${{ github.run_attempt }}.log
