name: E2E (Structured output)

# Live end-to-end check for the structured-output feature: drive the real
# `claude` CLI through `oneharness run --schema` and assert a schema-valid
# round-trip (scripts/e2e-schema.sh). This is the per-FEATURE counterpart to the
# per-harness e2e workflows — its job is to catch drift in Claude Code's native
# `--json-schema` flag and `structured_output` field, which the hermetic
# tests/cli.rs suite can only mock. Auth reuses the Claude secret, so no new one.
#
# Security: same posture as the per-harness workflows — gated to the canonical
# repo and never run for a fork PR head, so a fork PR can edit this file without
# the token ever being exposed (GitHub also withholds secrets from fork PRs).

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

permissions:
  contents: read

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

env:
  CARGO_TERM_COLOR: always
  # In CI the harness is installed and auth verified up front, so any e2e skip
  # means detection/install/spawn broke (e.g. an unresolved .cmd shim on Windows)
  # — fail instead of going green having tested nothing. See scripts/e2e-lib.sh.
  OH_E2E_NO_SKIP: 1

jobs:
  live:
    name: schema live e2e (${{ matrix.os }})
    if: >-
      github.repository == 'nickderobertis/oneharness' &&
      (github.event_name != 'pull_request' ||
       github.event.pull_request.head.repo.full_name == github.repository)
    strategy:
      fail-fast: false
      matrix:
        # Linux + macOS only. The native delivery passes the schema as a
        # quote-heavy JSON argument (`--json-schema '{...}'`), and an npm-installed
        # harness is a `.cmd` shim on Windows where cmd.exe's `%*` forwarding
        # mangles quote-containing arguments — a platform limitation, not a
        # oneharness bug. The hermetic `check` job (ci.yml) still exercises the
        # full structured-output argv + validation path on windows-latest.
        os: [ubuntu-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        shell: bash
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4

      - name: Verify the auth token is present
        env:
          CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
        run: |
          if [ -z "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]; then
            echo "::error::CLAUDE_CODE_OAUTH_TOKEN secret is not set. Mint one with 'claude setup-token', then sync it (just secrets-sync) or set it: gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo ${{ github.repository }}"
            exit 1
          fi

      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - uses: taiki-e/install-action@v2
        with:
          tool: just

      - uses: actions/setup-node@v4
        with:
          node-version: lts/*
      - name: Install Claude Code CLI
        run: |
          npm install -g @anthropic-ai/claude-code
          claude --version

      - name: Live e2e against the real CLI
        env:
          CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
          CLAUDE_E2E_MODEL: ${{ vars.CLAUDE_E2E_MODEL }}
        run: just live-schema
