Skip to content

build(deps): Bump astro from 6.3.1 to 6.4.6 in /web #1830

build(deps): Bump astro from 6.3.1 to 6.4.6 in /web

build(deps): Bump astro from 6.3.1 to 6.4.6 in /web #1830

name: Wheels Bot — Address Review
# OPT-IN implementer for reviewer findings. This stage no longer auto-fires:
# the converged-changes marker that used to trigger it died with the A/B
# critique loop (retired per maintainer decision 2026-06-11), and the
# auto-fire push chain had landed a broken spec on a PR (#3005). A human now
# opts a PR in by either:
# * applying the `bot-address-review` label to the PR, or
# * dispatching this workflow manually with the PR number.
# The bot reads the most recent wheels-bot review on the PR's current head
# SHA, applies the actionable findings, and pushes to the PR's existing
# branch. The new commit triggers a fresh Reviewer run on the new SHA.
#
# This is a *coding* stage — Opus model, broad allowlist with the test
# runner, mirrors propose-fix's setup. Different from the Reviewer, which is
# analytical.
#
# Fork-PR note: `pull_request: labeled` runs from a fork carry no vars or
# secrets, so the WHEELS_BOT_ENABLED gate reads empty and the job skips —
# fail-closed by construction (this stage pushes to the PR branch, which it
# could not do for a fork anyway).
on:
pull_request:
types: [labeled]
branches: [develop]
workflow_dispatch:
inputs:
pr-number:
description: 'PR number whose reviewer findings to address'
required: true
type: string
permissions:
contents: read
concurrency:
group: wheels-bot-address-review-${{ github.event.pull_request.number || inputs.pr-number }}
cancel-in-progress: false
jobs:
address-review:
name: Address review
runs-on: ubuntu-latest
timeout-minutes: 60
# Opt-in only: the maintainer-applied `bot-address-review` label
# (labeling requires triage access or higher — keep the triage role
# restricted, since this stage pushes commits) or a manual dispatch.
# No marker auto-fire path remains.
if: |
vars.WHEELS_BOT_ENABLED == 'true'
&& (
github.event_name == 'workflow_dispatch'
|| (github.event_name == 'pull_request'
&& github.event.label.name == 'bot-address-review')
)
env:
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr-number }}
WHEELS_CI: "true"
steps:
- name: Generate App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.WHEELS_BOT_APP_ID }}
private-key: ${{ secrets.WHEELS_BOT_PRIVATE_KEY }}
- name: Resolve PR head ref + SHA
id: pr
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
set -euo pipefail
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::pr-number must be numeric, got: $PR_NUMBER"
exit 1
fi
# Resolve the head ref (for the branch checkout below) AND the head
# SHA (threaded into the prompt as <head-sha>) in a single `gh pr
# view`, so the marker SHA is captured once here — at run start,
# exactly once — instead of being re-derived by the model
# mid-session (the #2848 race). Unlike bot-advisor.yml the checkout
# stays branch-name-keyed: this stage commits and pushes back, and a
# detached-HEAD SHA checkout would break `git push origin HEAD`. The
# captured SHA is therefore the head at run start — exactly the
# marker's <sha-before> (the head before this stage's own commit),
# and the SHA whose wheels-bot review the prompt consumes.
info=$(gh pr view "$PR_NUMBER" --repo wheels-dev/wheels --json headRefName,headRefOid)
ref=$(echo "$info" | python3 -c "import json,sys; print(json.load(sys.stdin)['headRefName'])")
sha=$(echo "$info" | python3 -c "import json,sys; print(json.load(sys.stdin)['headRefOid'])")
if [ -z "$ref" ]; then
echo "::error::Could not resolve PR head ref for #$PR_NUMBER"
exit 1
fi
if [ -z "$sha" ]; then
echo "::error::Could not resolve PR head SHA for #$PR_NUMBER"
exit 1
fi
echo "head=$ref" >> "$GITHUB_OUTPUT"
echo "sha=$sha" >> "$GITHUB_OUTPUT"
- name: Checkout PR branch
uses: actions/checkout@v6
with:
ref: ${{ steps.pr.outputs.head }}
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Skip check
id: gate
uses: ./.github/actions/wheels-bot-skip-check
with:
target-type: pr
target-number: ${{ env.PR_NUMBER }}
# Skip if address-held already engaged for this PR *at this head
# SHA* (the prompt emits the marker SHA-suffixed; matching the
# SHA-less prefix would permanently no-op every future label opt-in
# on the PR). The prompt does deeper round counting (outer-loop cap
# of 5).
marker-pattern: 'wheels-bot:address-held:${{ env.PR_NUMBER }}:${{ steps.pr.outputs.sha }}'
github-token: ${{ steps.app-token.outputs.token }}
- name: Configure git
if: steps.gate.outputs.skip == 'false'
run: |
git config user.name "wheels-bot[bot]"
git config user.email "wheels-bot[bot]@users.noreply.github.com"
- name: Set up Wheels test environment
if: steps.gate.outputs.skip == 'false'
uses: ./.github/actions/setup-wheels-test-env
with:
port: '60007'
install-playwright: 'false'
- name: Run Address Review
if: steps.gate.outputs.skip == 'false'
uses: anthropics/claude-code-action@v1
with:
# The labeled trigger is human-applied, but keep the bot identities
# allowed for consistency with the rest of the pipeline.
allowed_bots: 'wheels-bot[bot],github-actions[bot]'
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.app-token.outputs.token }}
# Thread the head SHA resolved in "Resolve PR head ref + SHA" — the
# head at run start, before this stage's own commit — into the prompt
# as a second argument. The implementer emits its
# `wheels-bot:address-review:<pr>:<sha>:` markers from this value
# instead of re-deriving it with `gh pr view`, which races with
# pushes landing mid-session and left the marker pointing at the
# wrong commit (issue #2848). The Run step's Bash allowlist is gh +
# read-only git + the test runner (no echo/printenv), so the model
# can't read a step env var — the SHA must travel in the prompt text,
# the same channel the PR number already uses.
prompt: |
/address-review ${{ env.PR_NUMBER }} ${{ steps.pr.outputs.sha }}
# Model policy: judging gate = fable, coding stages = opus, janitorial = sonnet.
claude_args: |
--model claude-opus-4-8
--max-turns 1000
--allowedTools "Bash(gh:*),Bash(git:*),Bash(bash tools/test-local.sh*),Bash(curl:*),Read,Edit,Write,Grep,Glob"
- name: Push branch
if: steps.gate.outputs.skip == 'false'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
# Only push if the bot actually committed changes; otherwise
# the run was a no-op (e.g., address-held safety net engaged).
if [ -z "$(git log @{u}..HEAD --oneline 2>/dev/null)" ]; then
echo "No new commits — nothing to push."
exit 0
fi
git push origin HEAD
- name: Stop Lucee server
if: always()
run: |
if [ -f /tmp/lucli-server.pid ]; then
kill $(cat /tmp/lucli-server.pid) 2>/dev/null || true
fi
lucli server stop 2>/dev/null || true