diff --git a/.github/workflows/feature-ideation-reusable.yml b/.github/workflows/feature-ideation-reusable.yml index f578bb79..59955abb 100644 --- a/.github/workflows/feature-ideation-reusable.yml +++ b/.github/workflows/feature-ideation-reusable.yml @@ -63,10 +63,10 @@ on: default: 'standard' type: string model: - description: > - Model family — `opus` (default, recommended), `sonnet`, or `haiku`. - The resolver/CLI maps the family to the current version ID. An - operator MAY override with a concrete model ID for a one-off pin. + # The Claude Code CLI resolves the family client-side (`--model opus` + # in the analyst step) to the current version ID — never pin a + # version ID here. See standards/agent-standards.md. + description: 'Model family (`opus` recommended - Sonnet produces shallower adversarial passes) or an explicit model id override.' required: false default: 'opus' type: string diff --git a/.github/workflows/feature-ideation-tests.yml b/.github/workflows/feature-ideation-tests.yml index 24c5989a..c0561a45 100644 --- a/.github/workflows/feature-ideation-tests.yml +++ b/.github/workflows/feature-ideation-tests.yml @@ -58,11 +58,31 @@ jobs: with: fetch-depth: 1 - - name: Install bats, shellcheck, and jq + - name: Install bats, shellcheck, jq, and yq run: | set -euo pipefail sudo apt-get update -qq sudo apt-get install -y --no-install-recommends bats shellcheck jq + # Install a pinned mikefarah/yq so the suite is reproducible and not + # dependent on whatever version ships in the runner image. + YQ_VERSION="v4.44.3" + # --proto '=https' --proto-redir '=https' enforces HTTPS on both the + # initial request and any redirects (GitHub releases redirect to + # objects.githubusercontent.com). + # --retry 5 --retry-delay 2 --retry-connrefused --retry-all-errors + # rides out the transient network/5xx blips from that redirect target + # that otherwise flake this suite (#739); --connect-timeout/--max-time + # bound each attempt so a stalled connection cannot eat the job budget. + curl -sSfL --proto '=https' --proto-redir '=https' --connect-timeout 10 --max-time 60 \ + --retry 5 --retry-delay 2 --retry-connrefused --retry-all-errors \ + "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" \ + -o /tmp/yq_linux_amd64 + # SHA-256 of yq_linux_amd64 for v4.44.3, hardcoded (vetted) rather than + # fetched from the same release, so a swapped release asset is detected. + # Update together with YQ_VERSION. + YQ_SHA256="a2c097180dd884a8d50c956ee16a9cec070f30a7947cf4ebf87d5f36213e9ed7" + echo "${YQ_SHA256} /tmp/yq_linux_amd64" | sha256sum -c - + sudo install -m 755 /tmp/yq_linux_amd64 /usr/local/bin/yq - name: Install Python jsonschema and PyYAML run: 'pip install --quiet --require-hashes --only-binary :all: -r scripts/feature-ideation-tests-requirements.txt' diff --git a/test/workflows/feature-ideation/model-input.bats b/test/workflows/feature-ideation/model-input.bats new file mode 100644 index 00000000..a0dd0976 --- /dev/null +++ b/test/workflows/feature-ideation/model-input.bats @@ -0,0 +1,39 @@ +#!/usr/bin/env bats +# Tests for the `model` input of the reusable feature-ideation workflow +# (issue #1210). The default must be the model FAMILY (`opus`) so the BMAD +# Analyst auto-tracks the current Opus via the Claude Code CLI's client-side +# alias resolution — a pinned version id must never be the default. + +load 'helpers/setup' + +WF="${TT_REPO_ROOT}/.github/workflows/feature-ideation-reusable.yml" + +@test "model input: default is the opus family" { + run yq -r '.on.workflow_call.inputs.model.default' "$WF" + [ "$status" -eq 0 ] + [ "$output" = "opus" ] +} + +@test "model input: default is not a pinned claude-* version id" { + run yq -r '.on.workflow_call.inputs.model.default' "$WF" + [ "$status" -eq 0 ] + [[ "$output" != claude-* ]] +} + +@test "model input: description recommends the opus family with an id override" { + run yq -r '.on.workflow_call.inputs.model.description' "$WF" + [ "$status" -eq 0 ] + [ "$output" = 'Model family (`opus` recommended - Sonnet produces shallower adversarial passes) or an explicit model id override.' ] +} + +@test "analyst step: model is passed via --model so the CLI resolves the family" { + run yq -r '.jobs[].steps[]? | select(.name == "Run Claude Code — BMAD Analyst") | .with.claude_args' "$WF" + [ "$status" -eq 0 ] + [[ "$output" == *'--model ${{ inputs.model }}'* ]] +} + +@test "analyst step: no ANTHROPIC_MODEL env bypasses the CLI's alias resolver" { + run yq -r '.jobs[].steps[]? | select(.name == "Run Claude Code — BMAD Analyst") | .env // {} | has("ANTHROPIC_MODEL")' "$WF" + [ "$status" -eq 0 ] + [ "$output" = "false" ] +}