# ultra11y on GitLab CI — the same adjudication the GitHub Action runs.
#
# There is no GitLab port of anything here. The engine is one file and the adjudication tier
# is one engine command, so what the composite action does in eleven steps is what this job
# does in eight lines. The only GitHub-shaped parts of the action — the event allowlist, the
# `$GITHUB_OUTPUT` plumbing between passes, `claude-code-action` itself — exist to work around
# a harness, and `adjudicate-runner: cli` is that same tier without one.
#
# Include it:
#
#   include:
#     - remote: 'https://raw.githubusercontent.com/maxgfr/ultra11y/main/skills/ultra11y/templates/gitlab-ci.yml'
#
#   variables:
#     ULTRA11Y_STANDARD: rgaa
#     ULTRA11Y_URL: 'http://127.0.0.1:8080/'
#
# and set CLAUDE_CODE_OAUTH_TOKEN as a MASKED, PROTECTED CI/CD variable
# (mint it with `claude setup-token`). ANTHROPIC_API_KEY works too.
#
# WITHOUT that variable the job still runs and still ships a report: the judgment criteria
# simply stay « to assess » rather than being silently called conforming. That degradation is
# the same one the GitHub Action makes on a fork's merge request, and it is deliberate.

variables:
  ULTRA11Y_STANDARD: wcag
  ULTRA11Y_LANG: en
  # Exact by default: a CI report must say which engine produced it. The release script keeps
  # this pin aligned with the package version; override only for a deliberate upgrade test.
  ULTRA11Y_VERSION: '5.40.0'
  # Batches of 8 amortise the prompt; each accepted verdict is checkpointed before the next
  # batch, and later passes receive only the residue.
  ULTRA11Y_GRAIN: batch
  ULTRA11Y_CRAWL_MAX: '0'
  # `false` is the economical report-only default. Set `pages` for a certification: every
  # criterion on every captured page must then have a verdict or the job fails.
  ULTRA11Y_REQUIRE_DECIDED: 'false'
  # A dollar ceiling per call. The turn budget the GitHub Action derives has no equivalent
  # here on purpose — `--max-turns` is not a flag of the Claude Code CLI, and the CLI swallows
  # unknown flags without a word, so it would look like a bound and be none.
  ULTRA11Y_BUDGET_USD: '0.50'
  # Pages to render before adjudicating. NOT optional if you care about the count: measured on
  # RGAA, 80 criteria need adjudicating from source alone and 41 once a page has been scanned.
  ULTRA11Y_URL: ''

.ultra11y:base:
  image: node:24
  variables:
    ENGINE: node_modules/ultra11y/scripts/ultra11y.mjs
  before_script:
    - npm i --no-save --save-exact "ultra11y@${ULTRA11Y_VERSION}"
    - test "$(node -p "require('ultra11y/package.json').version")" = "$ULTRA11Y_VERSION"

ultra11y:audit:
  extends: .ultra11y:base
  stage: test
  script:
    - node "$ENGINE" audit . --standard "$ULTRA11Y_STANDARD" --lang "$ULTRA11Y_LANG" --out audits --json > /dev/null
    # Render before you adjudicate, when a URL is in reach. A criterion about computed colour
    # or visible focus cannot be settled from source, and asking a model to try costs money to
    # be told so.
    - |
      if [ -n "$ULTRA11Y_URL" ]; then
        npx --yes playwright install --with-deps chromium
        node "$ENGINE" scan --crawl "$ULTRA11Y_URL" --max "$ULTRA11Y_CRAWL_MAX" \
          --runtime local --cwd "$PWD" --standard "$ULTRA11Y_STANDARD" \
          --merge audits/audit-latest.json --out audits
      fi

    # Replay what has already been ruled, BEFORE spending anything: a repository whose ledger
    # is current has nothing left to adjudicate and this job costs nothing.
    - |
      LEDGER=".ultra11y/verdicts/${ULTRA11Y_STANDARD}.json"
      if [ -f "$LEDGER" ]; then
        node "$ENGINE" verify --apply "$LEDGER" --in audits/audit-latest.json \
          --standard "$ULTRA11Y_STANDARD" --out audits
      fi

    # THE ADJUDICATION — the same command the GitHub Action's `adjudicate-runner: cli` runs.
    - |
      if [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}${ANTHROPIC_API_KEY:-}" ]; then
        npm i --no-save --prefix /tmp/a11y-adjudicator "@anthropic-ai/claude-code@2"
        export ULTRA11Y_CLAUDE_BIN=/tmp/a11y-adjudicator/node_modules/.bin/claude
        node "$ENGINE" judge --in audits/audit-latest.json --out audits --apply \
          --runner cli --grain "$ULTRA11Y_GRAIN" --max-budget-usd "$ULTRA11Y_BUDGET_USD" \
          --standard "$ULTRA11Y_STANDARD" --lang "$ULTRA11Y_LANG" --ledger "$LEDGER" \
          || echo "ultra11y: the adjudication failed — the judgment criteria stay to assess."
      else
        echo "ultra11y: no model credential — the judgment criteria stay to assess."
      fi

    - node "$ENGINE" report --in audits/audit-latest.json --out audits --standard "$ULTRA11Y_STANDARD" --lang "$ULTRA11Y_LANG"
    # The gate. `--fail-on bloquant` fails the job on a blocking non-conformity; add
    # `check --require-decided` to also fail while any criterion is still « to assess ».
    - |
      if [ -n "$ULTRA11Y_URL" ]; then
        node "$ENGINE" check --in audits/audit-latest.json --standard "$ULTRA11Y_STANDARD" --require-rendered
      fi
      if [ "$ULTRA11Y_REQUIRE_DECIDED" = "pages" ]; then
        node "$ENGINE" check --in audits/audit-latest.json --standard "$ULTRA11Y_STANDARD" --require-decided=pages
      elif [ "$ULTRA11Y_REQUIRE_DECIDED" = "true" ]; then
        node "$ENGINE" check --in audits/audit-latest.json --standard "$ULTRA11Y_STANDARD" --require-decided
      fi
    - node "$ENGINE" audit --in audits/audit-latest.json --standard "$ULTRA11Y_STANDARD" --fail-on bloquant
  artifacts:
    when: always
    paths:
      - audits/
    expire_in: 30 days
