Skip to content

Docs validation (agent) #19

Docs validation (agent)

Docs validation (agent) #19

name: Docs validation (agent)
on:
workflow_dispatch:
inputs:
mode:
description: 'Validation mode'
required: true
type: choice
default: 'api'
options:
- 'api'
- 'guide'
section:
description: '[api mode] API section to validate (ignored in guide mode)'
required: false
type: choice
default: 'Model Class'
options:
- 'Configuration'
- 'Controller'
- 'Global Helpers'
- 'Migrator'
- 'Model Class'
- 'Model Configuration'
- 'Model Object'
- 'View Helpers'
directory:
description: '[guide mode] Top-level guides directory to validate (ignored in api mode)'
required: false
type: choice
default: 'upgrading'
options:
- 'basics'
- 'command-line-tools'
- 'contributing'
- 'core-concepts'
- 'deployment'
- 'digging-deeper'
- 'start-here'
- 'testing'
- 'upgrading'
limit:
description: 'Max items to validate this run (blank = all pending)'
required: false
type: string
default: ''
model:
description: 'Anthropic model id'
required: false
type: string
default: 'claude-sonnet-4-6'
dry_run:
description: 'List targets without invoking the agent'
required: false
type: boolean
default: false
force:
description: 'Re-run already-done items (overwrites their state.json entries)'
required: false
type: boolean
default: false
permissions:
contents: write
pull-requests: write
concurrency:
group: docs-validation-${{ inputs.mode }}-${{ inputs.section || inputs.directory }}
cancel-in-progress: false
jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 240
steps:
- name: Checkout dispatching ref
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Install tools/docs-validation deps
working-directory: tools/docs-validation
run: npm ci --no-audit --no-fund
- name: Set up pnpm
if: ${{ inputs.mode == 'guide' }}
uses: pnpm/action-setup@v5
with:
version: 10.23.0
- name: Install web pnpm deps (for verify-docs harness in guide mode)
if: ${{ inputs.mode == 'guide' }}
working-directory: web
run: pnpm install --frozen-lockfile
- name: Set up Linuxbrew
uses: Homebrew/actions/setup-homebrew@master
- name: Install Wheels CLI
run: |
brew tap wheels-dev/wheels || true
brew install wheels
- name: Patch wheels wrapper JAVA_HOME for Linux
run: |
WRAPPER="$(brew --prefix wheels)/bin/wheels"
sed -i 's|/openjdk.jdk/Contents/Home||g' "$WRAPPER"
grep JAVA_HOME "$WRAPPER"
- name: Warm up wheels module
run: wheels --version >/dev/null
- name: Compute branch name + commit message
id: branch
run: |
if [[ "${{ inputs.mode }}" == "guide" ]]; then
slug=$(echo "${{ inputs.directory }}" | tr '[:upper:] ' '[:lower:]-')
echo "name=docs-validation/guide-${slug}-${{ github.run_id }}" >> "$GITHUB_OUTPUT"
echo "title=docs(guides): validate ${{ inputs.directory }} directory" >> "$GITHUB_OUTPUT"
echo "msg=docs(guides): validate ${{ inputs.directory }} directory (run ${{ github.run_id }})" >> "$GITHUB_OUTPUT"
echo "label=${{ inputs.directory }} directory" >> "$GITHUB_OUTPUT"
else
slug=$(echo "${{ inputs.section }}" | tr '[:upper:] ' '[:lower:]-')
echo "name=docs-validation/${slug}-${{ github.run_id }}" >> "$GITHUB_OUTPUT"
echo "title=docs(api): validate ${{ inputs.section }} section" >> "$GITHUB_OUTPUT"
echo "msg=docs(api): validate ${{ inputs.section }} section (run ${{ github.run_id }})" >> "$GITHUB_OUTPUT"
echo "label=${{ inputs.section }} section" >> "$GITHUB_OUTPUT"
fi
- name: Configure git
run: |
git config user.name "wheels-docs-validator[bot]"
git config user.email "wheels-docs-validator@users.noreply.github.com"
git checkout -b "${{ steps.branch.outputs.name }}"
- name: List targets (dry-run)
if: ${{ inputs.dry_run }}
working-directory: tools/docs-validation
run: |
ARGS=(--mode "${{ inputs.mode }}" --dry-run)
if [[ "${{ inputs.mode }}" == "guide" ]]; then
ARGS+=(--directory "${{ inputs.directory }}")
else
ARGS+=(--section "${{ inputs.section }}")
fi
if [[ -n "${{ inputs.limit }}" ]]; then
ARGS+=(--limit "${{ inputs.limit }}")
fi
if [[ "${{ inputs.force }}" == "true" ]]; then
ARGS+=(--force)
fi
node orchestrate.mjs "${ARGS[@]}"
- name: Run agent
if: ${{ !inputs.dry_run }}
working-directory: tools/docs-validation
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WHEELS_DOCS_MODEL: ${{ inputs.model }}
WHEELS_DOCS_MAX_TURNS: '24'
LUCLI_HOME: ${{ runner.temp }}/.wheels
WHEELS_FRAMEWORK_PATH: ${{ github.workspace }}/vendor/wheels
run: |
ARGS=(--mode "${{ inputs.mode }}")
if [[ "${{ inputs.mode }}" == "guide" ]]; then
ARGS+=(--directory "${{ inputs.directory }}")
else
ARGS+=(--section "${{ inputs.section }}")
fi
if [[ -n "${{ inputs.limit }}" ]]; then
ARGS+=(--limit "${{ inputs.limit }}")
fi
if [[ "${{ inputs.force }}" == "true" ]]; then
ARGS+=(--force)
fi
node orchestrate.mjs "${ARGS[@]}"
- name: Show diff
if: ${{ !inputs.dry_run }}
run: |
git status
git --no-pager diff --stat
echo "---"
git --no-pager diff | head -500 || true
- name: Commit + push if changes
if: ${{ !inputs.dry_run }}
id: commit
run: |
if git diff --quiet && git diff --cached --quiet; then
echo "No changes produced by the agent."
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git add -A
git commit -m "${{ steps.branch.outputs.msg }}"
git push -u origin "${{ steps.branch.outputs.name }}"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Open draft PR
if: ${{ !inputs.dry_run && steps.commit.outputs.changed == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BODY=$(cat <<'EOF'
## Summary
Agent-driven docs validation for the **${{ steps.branch.outputs.label }}**.
- Mode: `${{ inputs.mode }}`
- Edit scope (api mode): `vendor/wheels/**/*.cfc` (docblocks + narrow body fixes) and `vendor/wheels/public/docs/reference/{scope}/{name}.txt`
- Edit scope (guide mode): `web/sites/guides/src/content/docs/v4-0-0-snapshot/**/*.mdx?` and `vendor/wheels/**/*.cfc` (docblock prose only)
- Per-item status tracked in `tools/docs-validation/state.json`
## Review checklist
- [ ] Each `state.json` entry with `status: done` has the corresponding reference file (api) or annotated guide page (guide)
- [ ] No CFC function signature changes (agent is forbidden from changing them — verify anyway)
- [ ] Any docblock/body edits hold up against the test suite
- [ ] `state.json` items with `status: needs_human` describe the open question in `notes`
EOF
)
gh pr create \
--base develop \
--head "${{ steps.branch.outputs.name }}" \
--title "${{ steps.branch.outputs.title }}" \
--body "$BODY" \
--draft
- name: Upload state artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: docs-validation-state-${{ github.run_id }}
path: tools/docs-validation/state.json
if-no-files-found: ignore