Skip to content

Auto Optimization Draft PR #7

Auto Optimization Draft PR

Auto Optimization Draft PR #7

name: Auto Optimization Draft PR
"on":
issues:
types: [opened, edited, reopened, labeled]
workflow_dispatch:
inputs:
issue_number:
description: "Monthly optimization task issue number"
required: true
jobs:
auto-pr:
if: contains(github.event.issue.labels.*.name, 'monthly-optimization-task') || inputs.issue_number != ''
runs-on: ubuntu-latest
permissions:
actions: write
contents: write
issues: write
pull-requests: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check automation prerequisites
id: prereqs
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
mkdir -p data/output/auto_optimization
if [ -z "${ANTHROPIC_API_KEY}" ]; then
echo "has_anthropic_key=false" >> "$GITHUB_OUTPUT"
echo "Claude automation skipped: ANTHROPIC_API_KEY is not configured for this repo." > data/output/auto_optimization/skip_reason.txt
else
echo "has_anthropic_key=true" >> "$GITHUB_OUTPUT"
fi
- name: Load issue context
id: issue_context
run: |
python3 - <<'PY'
import json
import os
import urllib.request
from pathlib import Path
repo = os.environ["GITHUB_REPOSITORY"]
issue_number = os.environ["ISSUE_NUMBER"]
token = os.environ["GITHUB_TOKEN"]
api_url = f"https://api.github.com/repos/{repo}/issues/{issue_number}"
request = urllib.request.Request(
api_url,
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "auto-optimization-pr",
},
)
with urllib.request.urlopen(request) as response:
issue = json.load(response)
issue_context = {
"number": issue["number"],
"title": issue["title"],
"body": issue.get("body", ""),
}
output_dir = Path("data/output/auto_optimization")
output_dir.mkdir(parents=True, exist_ok=True)
(output_dir / "issue_context.json").write_text(
json.dumps(issue_context, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
print("issue_title<<EOF", file=output)
print(issue_context["title"], file=output)
print("EOF", file=output)
print("issue_body<<EOF", file=output)
print(issue_context["body"], file=output)
print("EOF", file=output)
PY
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ inputs.issue_number || github.event.issue.number }}
- name: Prepare auto optimization payload
id: auto_payload
run: |
python3 scripts/prepare_auto_optimization_pr.py --issue-context-file data/output/auto_optimization/issue_context.json --output-dir data/output/auto_optimization >> "$GITHUB_OUTPUT"
- name: Append task summary
run: cat data/output/auto_optimization/task_summary.md >> "$GITHUB_STEP_SUMMARY"
- name: Export selected task summary
id: selected_tasks
run: |
{
echo "task_summary<<EOF"
cat data/output/auto_optimization/task_summary.md
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Append skip reason
if: steps.prereqs.outputs.has_anthropic_key != 'true' || steps.auto_payload.outputs.should_run != 'true'
run: |
if [ -f data/output/auto_optimization/skip_reason.txt ]; then
cat data/output/auto_optimization/skip_reason.txt >> "$GITHUB_STEP_SUMMARY"
else
echo "No eligible low-risk auto-pr-safe tasks were found; skipping automation." >> "$GITHUB_STEP_SUMMARY"
fi
- name: Prepare automation branch
if: steps.prereqs.outputs.has_anthropic_key == 'true' && steps.auto_payload.outputs.should_run == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B "${{ steps.auto_payload.outputs.branch_name }}"
- name: Run Claude auto optimization
if: steps.prereqs.outputs.has_anthropic_key == 'true' && steps.auto_payload.outputs.should_run == 'true'
timeout-minutes: 15
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
use_bedrock: false
use_vertex: false
claude_args: --max-turns 8
prompt: |
Do not ask for additional approval.
Do not create a pull request yourself. The workflow will handle git, PR creation, CI dispatch, and post-CI merge.
Only implement the low-risk tasks explicitly marked `[auto-pr-safe]`.
Ignore any medium-risk or high-risk tasks.
You are working inside CryptoStrategies, the shared strategy-logic repository.
Prefer minimal changes in documentation, report wording, validation, shadow/challenger plumbing, instrumentation, and tests.
Do not change production selector logic or ranking behavior from this issue alone.
If an eligible task is marked `experiment-only`, keep the change non-production.
Never edit files under src/ in this automation step.
If the selected low-risk tasks already appear implemented on the current main branch, leave the working tree unchanged.
Do not use Bash in this workflow. Limit yourself to file edits and repository-local reasoning.
The workflow will run CI after the PR is created.
## Issue Title
${{ steps.issue_context.outputs.issue_title }}
## Selected Low-Risk Tasks
${{ steps.selected_tasks.outputs.task_summary }}
- name: Detect changes
id: changes
if: steps.prereqs.outputs.has_anthropic_key == 'true' && steps.auto_payload.outputs.should_run == 'true'
run: |
if git diff --quiet; then
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Evaluate merge guardrails
id: merge_guard
if: steps.changes.outputs.has_changes == 'true'
run: |
git diff --name-only --relative > data/output/auto_optimization/changed_files.txt
python3 - <<'PY'
import json
import os
from pathlib import Path
from scripts.prepare_auto_optimization_pr import evaluate_changed_files
output_dir = Path("data/output/auto_optimization")
payload = json.loads((output_dir / "payload.json").read_text(encoding="utf-8"))
changed_files = [
line.strip()
for line in (output_dir / "changed_files.txt").read_text(encoding="utf-8").splitlines()
if line.strip()
]
guard = evaluate_changed_files(changed_files)
merge_ready = bool(payload.get("task_level_auto_merge_allowed")) and bool(guard["allowed"])
if not payload.get("task_level_auto_merge_allowed"):
reason = "task_level_guard"
elif not guard["allowed"]:
reason = "sensitive_changed_files"
else:
reason = "ready"
summary_lines = [
"## Merge Guardrails",
f"- Task-level auto-merge eligible: `{ 'yes' if payload.get('task_level_auto_merge_allowed') else 'no' }`",
f"- Changed files reviewed: `{len(changed_files)}`",
f"- Sensitive files touched: `{len(guard['blocked_files'])}`",
]
if guard["blocked_files"]:
summary_lines.append("")
summary_lines.append("### Sensitive changed files")
summary_lines.extend(f"- `{path}`" for path in guard["blocked_files"])
if merge_ready:
summary_lines.extend(["", "Ready PR is allowed; the follow-up auto-merge workflow may merge after CI succeeds."])
else:
summary_lines.extend(["", f"PR will stay draft. Guard reason: `{reason}`"])
(output_dir / "guard_summary.md").write_text("\n".join(summary_lines).strip() + "\n", encoding="utf-8")
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
print(f"merge_ready={'true' if merge_ready else 'false'}", file=output)
print(f"guard_reason={reason}", file=output)
print(f"blocked_file_count={len(guard['blocked_files'])}", file=output)
PY
- name: Append merge guard summary
if: steps.changes.outputs.has_changes == 'true'
run: cat data/output/auto_optimization/guard_summary.md >> "$GITHUB_STEP_SUMMARY"
- name: Commit and push automation branch
if: steps.changes.outputs.has_changes == 'true'
run: |
git add -A
git commit -m "${{ steps.auto_payload.outputs.commit_message }}"
git push --force-with-lease origin "${{ steps.auto_payload.outputs.branch_name }}"
- name: Create or update automation PR
id: automation_pr
if: steps.changes.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH_NAME="${{ steps.auto_payload.outputs.branch_name }}"
PR_TITLE="${{ steps.auto_payload.outputs.pr_title }}"
PR_BODY_FILE="${{ steps.auto_payload.outputs.pr_body_file }}"
MERGE_READY="${{ steps.merge_guard.outputs.merge_ready }}"
EXISTING_PR_NUMBER=$(gh pr list --state open --head "${BRANCH_NAME}" --json number --jq '.[0].number // empty')
if [ -n "${EXISTING_PR_NUMBER}" ]; then
gh pr edit "${EXISTING_PR_NUMBER}" --title "${PR_TITLE}" --body-file "${PR_BODY_FILE}"
if [ "${MERGE_READY}" = "true" ]; then
gh pr ready "${EXISTING_PR_NUMBER}" || true
PR_STATE="ready_for_review"
else
gh pr ready "${EXISTING_PR_NUMBER}" --undo || true
PR_STATE="draft"
fi
PR_URL=$(gh pr view "${EXISTING_PR_NUMBER}" --json url --jq '.url')
PR_NUMBER="${EXISTING_PR_NUMBER}"
echo "pr_action=updated" >> "$GITHUB_OUTPUT"
else
CREATE_ARGS=(--base main --head "${BRANCH_NAME}" --title "${PR_TITLE}" --body-file "${PR_BODY_FILE}")
if [ "${MERGE_READY}" != "true" ]; then
CREATE_ARGS=(--draft "${CREATE_ARGS[@]}")
fi
PR_URL=$(gh pr create "${CREATE_ARGS[@]}")
PR_NUMBER=$(gh pr view "${PR_URL}" --json number --jq '.number')
if [ "${MERGE_READY}" = "true" ]; then
PR_STATE="ready_for_review"
else
PR_STATE="draft"
fi
echo "pr_action=created" >> "$GITHUB_OUTPUT"
fi
echo "pr_url=${PR_URL}" >> "$GITHUB_OUTPUT"
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
echo "pr_state=${PR_STATE}" >> "$GITHUB_OUTPUT"
- name: Dispatch CI workflow on automation branch
if: steps.changes.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run ci.yml --ref "${{ steps.auto_payload.outputs.branch_name }}"
- name: Append automation result
if: steps.prereqs.outputs.has_anthropic_key == 'true' && steps.auto_payload.outputs.should_run == 'true'
run: |
if [ "${{ steps.changes.outputs.has_changes }}" = "true" ]; then
{
echo ""
echo "## Automation PR Result"
echo "- PR ${{ steps.automation_pr.outputs.pr_action }}: ${{ steps.automation_pr.outputs.pr_url }}"
echo "- PR state: `${{ steps.automation_pr.outputs.pr_state }}`"
echo "- Guard reason: `${{ steps.merge_guard.outputs.guard_reason }}`"
echo "- CI workflow dispatched on branch: `${{ steps.auto_payload.outputs.branch_name }}`"
} >> "$GITHUB_STEP_SUMMARY"
if [ "${{ steps.merge_guard.outputs.merge_ready }}" = "true" ]; then
echo "Auto-merge will be handled only after a successful CI workflow run." >> "$GITHUB_STEP_SUMMARY"
fi
else
echo "No code changes were produced for the selected low-risk tasks." >> "$GITHUB_STEP_SUMMARY"
fi