Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
ignore:
- dependency-name: "anthropic"
- dependency-name: "claude-agent-sdk"
# Auto-merge minor and patch updates
pull-request-branch-name:
separator: "-"
commit-message:
Expand Down
115 changes: 115 additions & 0 deletions .github/workflows/ci-failure-resolver-with-agent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: CI Failure Resolver

on:
workflow_dispatch:
inputs:
prs:
description: 'PR(s): "123", "123,456", or "all"'
required: true
max_attempts:
description: "Max fix attempts per PR"
default: "3"

jobs:
gather-prs:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.get.outputs.matrix }}
steps:
- id: get
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ inputs.prs }}" = "all" ]; then
PRS=$(gh pr list --state open --json number,statusCheckRollup \
--jq '[.[] | select(.statusCheckRollup[]? | .status == "COMPLETED" and .conclusion == "FAILURE") | .number] | unique')
else
PRS=$(echo "${{ inputs.prs }}" | tr ',' '\n' | jq -R 'tonumber' | jq -sc .)
fi
echo "matrix={\"pr\":${PRS:-[]}}" >> $GITHUB_OUTPUT

fix-pr:
needs: gather-prs
if: fromJson(needs.gather-prs.outputs.matrix).pr[0] != null
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.gather-prs.outputs.matrix) }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
REPO: ${{ github.repository }}
PR: ${{ matrix.pr }}
MAX: ${{ inputs.max_attempts }}
steps:
- name: Analyze and classify
id: analyze
run: |
# Get attempt count
ATTEMPT=$(gh pr view $PR --repo $REPO --json labels \
--jq '[.labels[].name | capture("ci-fix-attempt-(?<n>[0-9]+)").n // empty | tonumber] | max // 0')
NEXT=$((ATTEMPT + 1))
echo "attempt=$NEXT" >> $GITHUB_OUTPUT

[ "$NEXT" -gt "$MAX" ] && echo "action=escalate" >> $GITHUB_OUTPUT && exit 0

# Fetch logs
HEAD=$(gh pr view $PR --repo $REPO --json headRefOid --jq '.headRefOid')
RUN_ID=$(gh api "repos/$REPO/actions/runs?head_sha=${HEAD}&status=failure&per_page=1" --jq '.workflow_runs[0].id // empty')
[ -z "$RUN_ID" ] && echo "action=skip" >> $GITHUB_OUTPUT && exit 0

gh run view "$RUN_ID" --repo $REPO --log-failed 2>&1 | tail -500 > /tmp/logs.txt
echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT

# Classify with Claude
LOGS=$(cat /tmp/logs.txt | jq -Rs .)
RESULT=$(curl -s https://api.anthropic.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d "{\"model\":\"claude-sonnet-4-20250514\",\"max_tokens\":256,\"messages\":[{\"role\":\"user\",\"content\":\"Classify this CI failure as JSON only: {\\\"category\\\": \\\"flake_network|flake_timeout|flake_race|error_code|error_test|unknown\\\", \\\"summary\\\": \\\"one sentence\\\", \\\"is_flake\\\": bool}\\n\\nLogs:\\n${LOGS}\"}]}" \
| jq -r '.content[0].text')

echo "classification=$(echo "$RESULT" | jq -c .)" >> $GITHUB_OUTPUT
IS_FLAKE=$(echo "$RESULT" | jq -r '.is_flake')

[ "$IS_FLAKE" = "true" ] && [ "$ATTEMPT" -eq 0 ] && echo "action=retry" >> $GITHUB_OUTPUT && exit 0
echo "action=fix" >> $GITHUB_OUTPUT

- name: Retry flaky CI
if: steps.analyze.outputs.action == 'retry'
run: gh run rerun ${{ steps.analyze.outputs.run_id }} --repo $REPO --failed

- name: Escalate
if: steps.analyze.outputs.action == 'escalate'
run: |
gh label create ci-needs-human --repo $REPO --color D93F0B 2>/dev/null || true
gh pr edit $PR --repo $REPO --add-label ci-needs-human
gh pr comment $PR --repo $REPO --body "🚨 CI fix bot reached max attempts ($MAX). Manual fix needed."

- name: Checkout & label
if: steps.analyze.outputs.action == 'fix'
uses: actions/checkout@v4
with:
ref: refs/pull/${{ matrix.pr }}/head
fetch-depth: 0

- if: steps.analyze.outputs.action == 'fix'
run: |
PREV=$((${{ steps.analyze.outputs.attempt }} - 1))
gh pr edit $PR --repo $REPO --remove-label "ci-fix-attempt-${PREV}" 2>/dev/null || true
gh label create "ci-fix-attempt-${{ steps.analyze.outputs.attempt }}" --repo $REPO --color FFA500 2>/dev/null || true
gh pr edit $PR --repo $REPO --add-label "ci-fix-attempt-${{ steps.analyze.outputs.attempt }}"

- name: Fix with agent
if: steps.analyze.outputs.action == 'fix'
uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Fix CI on PR #${{ env.PR }} (attempt ${{ steps.analyze.outputs.attempt }}/${{ env.MAX }}).
Classification: ${{ steps.analyze.outputs.classification }}
Logs:
$(cat /tmp/logs.txt)

Flake → harden test. Error → fix bug. Commit as "fix(ci): <summary>" and push.
134 changes: 134 additions & 0 deletions .github/workflows/claude-sdk-options-drift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Claude SDK Options Drift Check

on:
schedule:
- cron: '0 6 * * 1' # Weekly Monday 6am UTC
workflow_dispatch:

concurrency:
group: claude-sdk-options-drift
cancel-in-progress: true

jobs:
check-drift:
name: Check SDK Options Drift
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'

- name: Install claude-agent-sdk
run: pip install claude-agent-sdk

- name: Extract current SDK options
run: |
python3 -c "
import json
try:
from claude_agent_sdk import ClaudeAgentOptions
# Pydantic model — extract fields from model_fields
if hasattr(ClaudeAgentOptions, 'model_fields'):
fields = {}
for name, field_info in ClaudeAgentOptions.model_fields.items():
annotation = str(field_info.annotation) if field_info.annotation else 'unknown'
fields[name] = {
'type': annotation,
'required': field_info.is_required(),
}
else:
# Fallback: inspect __init__ signature
import inspect
sig = inspect.signature(ClaudeAgentOptions.__init__)
fields = {}
for name, param in sig.parameters.items():
if name == 'self':
continue
fields[name] = {
'type': str(param.annotation) if param.annotation != inspect.Parameter.empty else 'unknown',
'required': param.default == inspect.Parameter.empty,
}
print(json.dumps(fields, indent=2, sort_keys=True))
except ImportError:
print('{}')
import sys
print('WARNING: claude-agent-sdk not found', file=sys.stderr)
sys.exit(1)
" > /tmp/current-sdk-options.json

- name: Compare with manifest
id: check
run: |
python3 -c "
import json, sys

manifest_path = 'components/runners/ambient-runner/sdk-options-manifest.json'
try:
with open(manifest_path) as f:
manifest = json.load(f)
except FileNotFoundError:
print('No manifest file found — will create initial manifest')
sys.exit(1)

with open('/tmp/current-sdk-options.json') as f:
current = json.load(f)

manifest_keys = set(manifest.get('options', {}).keys())
current_keys = set(current.keys())

new_keys = sorted(current_keys - manifest_keys)
removed_keys = sorted(manifest_keys - current_keys)

if new_keys or removed_keys:
if new_keys:
print(f'New options: {new_keys}')
if removed_keys:
print(f'Removed options: {removed_keys}')
sys.exit(1)

print('No drift detected')
" || echo "drift=true" >> "$GITHUB_OUTPUT"

- name: Update manifest and create PR
if: steps.check.outputs.drift == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Build updated manifest
python3 -c "
import json, datetime

with open('/tmp/current-sdk-options.json') as f:
options = json.load(f)

manifest = {
'description': 'Canonical list of Claude Agent SDK ClaudeAgentOptions fields',
'generatedFrom': 'claude-agent-sdk (PyPI)',
'generatedAt': datetime.datetime.now(datetime.timezone.utc).isoformat(),
'options': options,
}

with open('components/runners/ambient-runner/sdk-options-manifest.json', 'w') as f:
json.dump(manifest, f, indent=2, sort_keys=True)
f.write('\n')
"

BRANCH="auto/sdk-options-update-$(date +%Y%m%d)"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "${BRANCH}"
git add components/runners/ambient-runner/sdk-options-manifest.json
git commit -m "chore: update Claude SDK options manifest"
git push origin "${BRANCH}"

gh pr create \
--title "chore: update Claude SDK options manifest" \
--body "Auto-detected changes in ClaudeAgentOptions fields from claude-agent-sdk on PyPI. The advanced-sdk-options UI component may need updating to expose new options." \
--label "amber:auto-fix"
123 changes: 0 additions & 123 deletions .github/workflows/component-benchmarks.yml

This file was deleted.

Loading