9393 - name : Prepare auto optimization payload
9494 id : auto_payload
9595 run : |
96- python3 scripts/prepare_auto_optimization_pr.py \
97- --issue-context-file data/output/auto_optimization/issue_context.json \
98- --output-dir data/output/auto_optimization >> "$GITHUB_OUTPUT"
96+ 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"
9997
10098 - name : Append task summary
10199 run : cat data/output/auto_optimization/task_summary.md >> "$GITHUB_STEP_SUMMARY"
@@ -115,7 +113,7 @@ jobs:
115113 if [ -f data/output/auto_optimization/skip_reason.txt ]; then
116114 cat data/output/auto_optimization/skip_reason.txt >> "$GITHUB_STEP_SUMMARY"
117115 else
118- echo "No eligible low-risk auto-pr-safe tasks were found; skipping draft PR generation ." >> "$GITHUB_STEP_SUMMARY"
116+ echo "No eligible low-risk auto-pr-safe tasks were found; skipping automation ." >> "$GITHUB_STEP_SUMMARY"
119117 fi
120118
121119 - name : Prepare automation branch
@@ -137,16 +135,17 @@ jobs:
137135 claude_args : --max-turns 8
138136 prompt : |
139137 Do not ask for additional approval.
140- Do not create a pull request yourself. The workflow will handle git, draft PR creation, and CI dispatch .
138+ Do not create a pull request yourself. The workflow will handle git, PR creation, CI dispatch, and post- CI merge .
141139 Only implement the low-risk tasks explicitly marked `[auto-pr-safe]`.
142140 Ignore any medium-risk or high-risk tasks.
143141 You are working inside CryptoLeaderRotation, the upstream selector repository.
144142 Prefer minimal changes in documentation, report wording, validation, shadow/challenger plumbing, instrumentation, and tests.
145143 Do not change production selector logic or ranking behavior from this issue alone.
146144 If an eligible task is marked `experiment-only`, keep the change non-production.
145+ Never edit files under src/ or config/ in this automation step.
147146 If the selected low-risk tasks already appear implemented on the current main branch, leave the working tree unchanged.
148147 Do not use Bash in this workflow. Limit yourself to file edits and repository-local reasoning.
149- The workflow will run CI after the draft PR is created.
148+ The workflow will run CI after the PR is created.
150149
151150 ## Issue Title
152151 ${{ steps.issue_context.outputs.issue_title }}
@@ -164,32 +163,107 @@ jobs:
164163 echo "has_changes=true" >> "$GITHUB_OUTPUT"
165164 fi
166165
166+ - name : Evaluate merge guardrails
167+ id : merge_guard
168+ if : steps.changes.outputs.has_changes == 'true'
169+ run : |
170+ git diff --name-only --relative > data/output/auto_optimization/changed_files.txt
171+ python3 - <<'PY'
172+ import json
173+ import os
174+ from pathlib import Path
175+
176+ from scripts.prepare_auto_optimization_pr import evaluate_changed_files
177+
178+ output_dir = Path("data/output/auto_optimization")
179+ payload = json.loads((output_dir / "payload.json").read_text(encoding="utf-8"))
180+ changed_files = [
181+ line.strip()
182+ for line in (output_dir / "changed_files.txt").read_text(encoding="utf-8").splitlines()
183+ if line.strip()
184+ ]
185+ guard = evaluate_changed_files(changed_files)
186+ merge_ready = bool(payload.get("task_level_auto_merge_allowed")) and bool(guard["allowed"])
187+ if not payload.get("task_level_auto_merge_allowed"):
188+ reason = "task_level_guard"
189+ elif not guard["allowed"]:
190+ reason = "sensitive_changed_files"
191+ else:
192+ reason = "ready"
193+
194+ summary_lines = [
195+ "## Merge Guardrails",
196+ f"- Task-level auto-merge eligible: `{ 'yes' if payload.get('task_level_auto_merge_allowed') else 'no' }`",
197+ f"- Changed files reviewed: `{len(changed_files)}`",
198+ f"- Sensitive files touched: `{len(guard['blocked_files'])}`",
199+ ]
200+ if guard["blocked_files"]:
201+ summary_lines.append("")
202+ summary_lines.append("### Sensitive changed files")
203+ summary_lines.extend(f"- `{path}`" for path in guard["blocked_files"])
204+ if merge_ready:
205+ summary_lines.extend(["", "Ready PR is allowed; the follow-up auto-merge workflow may merge after CI succeeds."])
206+ else:
207+ summary_lines.extend(["", f"PR will stay draft. Guard reason: `{reason}`"])
208+
209+ (output_dir / "guard_summary.md").write_text("\n".join(summary_lines).strip() + "\n", encoding="utf-8")
210+ with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
211+ print(f"merge_ready={'true' if merge_ready else 'false'}", file=output)
212+ print(f"guard_reason={reason}", file=output)
213+ print(f"blocked_file_count={len(guard['blocked_files'])}", file=output)
214+ PY
215+
216+ - name : Append merge guard summary
217+ if : steps.changes.outputs.has_changes == 'true'
218+ run : cat data/output/auto_optimization/guard_summary.md >> "$GITHUB_STEP_SUMMARY"
219+
167220 - name : Commit and push automation branch
168221 if : steps.changes.outputs.has_changes == 'true'
169222 run : |
170223 git add -A
171224 git commit -m "${{ steps.auto_payload.outputs.commit_message }}"
172225 git push --force-with-lease origin "${{ steps.auto_payload.outputs.branch_name }}"
173226
174- - name : Create or update draft PR
175- id : draft_pr
227+ - name : Create or update automation PR
228+ id : automation_pr
176229 if : steps.changes.outputs.has_changes == 'true'
177230 env :
178231 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
179232 run : |
180233 BRANCH_NAME="${{ steps.auto_payload.outputs.branch_name }}"
181234 PR_TITLE="${{ steps.auto_payload.outputs.pr_title }}"
182235 PR_BODY_FILE="${{ steps.auto_payload.outputs.pr_body_file }}"
236+ MERGE_READY="${{ steps.merge_guard.outputs.merge_ready }}"
183237 EXISTING_PR_NUMBER=$(gh pr list --state open --head "${BRANCH_NAME}" --json number --jq '.[0].number // empty')
184238 if [ -n "${EXISTING_PR_NUMBER}" ]; then
185239 gh pr edit "${EXISTING_PR_NUMBER}" --title "${PR_TITLE}" --body-file "${PR_BODY_FILE}"
240+ if [ "${MERGE_READY}" = "true" ]; then
241+ gh pr ready "${EXISTING_PR_NUMBER}" || true
242+ PR_STATE="ready_for_review"
243+ else
244+ gh pr ready "${EXISTING_PR_NUMBER}" --undo || true
245+ PR_STATE="draft"
246+ fi
186247 PR_URL=$(gh pr view "${EXISTING_PR_NUMBER}" --json url --jq '.url')
248+ PR_NUMBER="${EXISTING_PR_NUMBER}"
187249 echo "pr_action=updated" >> "$GITHUB_OUTPUT"
188250 else
189- PR_URL=$(gh pr create --draft --base main --head "${BRANCH_NAME}" --title "${PR_TITLE}" --body-file "${PR_BODY_FILE}")
251+ CREATE_ARGS=(--base main --head "${BRANCH_NAME}" --title "${PR_TITLE}" --body-file "${PR_BODY_FILE}")
252+ if [ "${MERGE_READY}" != "true" ]; then
253+ CREATE_ARGS=(--draft "${CREATE_ARGS[@]}")
254+ fi
255+ PR_URL=$(gh pr create "${CREATE_ARGS[@]}")
256+ PR_NUMBER=$(gh pr view "${PR_URL}" --json number --jq '.number')
257+ if [ "${MERGE_READY}" = "true" ]; then
258+ PR_STATE="ready_for_review"
259+ else
260+ PR_STATE="draft"
261+ fi
190262 echo "pr_action=created" >> "$GITHUB_OUTPUT"
191263 fi
192264 echo "pr_url=${PR_URL}" >> "$GITHUB_OUTPUT"
265+ echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
266+ echo "pr_state=${PR_STATE}" >> "$GITHUB_OUTPUT"
193267
194268 - name : Dispatch CI workflow on automation branch
195269 if : steps.changes.outputs.has_changes == 'true'
@@ -203,10 +277,15 @@ jobs:
203277 if [ "${{ steps.changes.outputs.has_changes }}" = "true" ]; then
204278 {
205279 echo ""
206- echo "## Draft PR Result"
207- echo "- Draft PR ${{ steps.draft_pr.outputs.pr_action }}: ${{ steps.draft_pr.outputs.pr_url }}"
280+ echo "## Automation PR Result"
281+ echo "- PR ${{ steps.automation_pr.outputs.pr_action }}: ${{ steps.automation_pr.outputs.pr_url }}"
282+ echo "- PR state: `${{ steps.automation_pr.outputs.pr_state }}`"
283+ echo "- Guard reason: `${{ steps.merge_guard.outputs.guard_reason }}`"
208284 echo "- CI workflow dispatched on branch: `${{ steps.auto_payload.outputs.branch_name }}`"
209285 } >> "$GITHUB_STEP_SUMMARY"
286+ if [ "${{ steps.merge_guard.outputs.merge_ready }}" = "true" ]; then
287+ echo "Auto-merge will be handled only after a successful CI workflow run." >> "$GITHUB_STEP_SUMMARY"
288+ fi
210289 else
211290 echo "No code changes were produced for the selected low-risk tasks." >> "$GITHUB_STEP_SUMMARY"
212291 fi
0 commit comments