From d2d7e8f8123f782d2c2d10453adda5fb2adc33b1 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Sun, 24 May 2026 15:41:17 +0800 Subject: [PATCH] chore: remove legacy source-local AI workflows --- .github/workflows/ai_review.yml | 233 -------------- .../workflows/auto_merge_optimization_pr.yml | 2 +- .github/workflows/auto_optimization_pr.yml | 291 ------------------ .../monthly_optimization_planner.yml | 55 ---- README.md | 5 +- README.zh-CN.md | 3 +- docs/operator_runbook.md | 4 +- scripts/build_monthly_optimization_plan.py | 2 +- ...st_auto_optimization_pr_workflow_config.py | 31 +- tests/test_build_monthly_optimization_plan.py | 11 +- ...ly_optimization_planner_workflow_config.py | 14 +- tests/test_monthly_publish_workflow_config.py | 31 +- 12 files changed, 24 insertions(+), 658 deletions(-) delete mode 100644 .github/workflows/ai_review.yml delete mode 100644 .github/workflows/auto_optimization_pr.yml diff --git a/.github/workflows/ai_review.yml b/.github/workflows/ai_review.yml deleted file mode 100644 index 0be33a6..0000000 --- a/.github/workflows/ai_review.yml +++ /dev/null @@ -1,233 +0,0 @@ -name: AI Monthly Review - -"on": - issues: - types: [opened] - workflow_dispatch: - inputs: - issue_number: - description: "Issue number to review" - required: true - -jobs: - ai-review: - if: vars.LEGACY_AI_REVIEW_ENABLED == 'true' && (contains(github.event.issue.labels.*.name, 'monthly-review') || inputs.issue_number != '') - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - issues: write - - env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Load review issue context - id: issue_context - run: | - mkdir -p data/output/ai_review - 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": "crypto-leader-rotation-ai-review", - }, - ) - with urllib.request.urlopen(request) as response: - issue = json.load(response) - - issue_context = { - "number": issue["number"], - "title": issue["title"], - "body": issue["body"], - } - Path("data/output/ai_review/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<> "$GITHUB_OUTPUT" - echo "Legacy 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<> "$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<> "$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 CryptoSnapshotPipelines, the upstream selector 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/ or config/ 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 diff --git a/.github/workflows/monthly_optimization_planner.yml b/.github/workflows/monthly_optimization_planner.yml index 1dca1a8..cdb0768 100644 --- a/.github/workflows/monthly_optimization_planner.yml +++ b/.github/workflows/monthly_optimization_planner.yml @@ -21,49 +21,6 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Detect GitHub App credentials - id: cross_repo_credentials - env: - APP_ID: ${{ vars.CROSS_REPO_GITHUB_APP_ID }} - APP_PRIVATE_KEY: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }} - run: | - if [ -n "$APP_ID" ] && [ -n "$APP_PRIVATE_KEY" ]; then - echo "has_app_credentials=true" >> "$GITHUB_OUTPUT" - else - echo "has_app_credentials=false" >> "$GITHUB_OUTPUT" - fi - - - name: Create GitHub App token for cross-repo access - id: cross_repo_app_token - if: steps.cross_repo_credentials.outputs.has_app_credentials == 'true' - uses: actions/create-github-app-token@v3 - with: - app-id: ${{ vars.CROSS_REPO_GITHUB_APP_ID }} - private-key: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }} - owner: ${{ github.repository_owner }} - repositories: | - CryptoSnapshotPipelines - CryptoStrategies - permission-actions: write - permission-issues: write - - - name: Resolve cross-repo access token - id: cross_repo_token - env: - APP_TOKEN: ${{ steps.cross_repo_app_token.outputs.token }} - FALLBACK_TOKEN: ${{ secrets.CROSS_REPO_GITHUB_TOKEN }} - run: | - if [ -n "$APP_TOKEN" ]; then - echo "token=$APP_TOKEN" >> "$GITHUB_OUTPUT" - echo "source=github_app" >> "$GITHUB_OUTPUT" - elif [ -n "$FALLBACK_TOKEN" ]; then - echo "token=$FALLBACK_TOKEN" >> "$GITHUB_OUTPUT" - echo "source=personal_access_token" >> "$GITHUB_OUTPUT" - else - echo "Missing cross-repo credentials. Configure GitHub App secrets or CROSS_REPO_GITHUB_TOKEN." >&2 - exit 1 - fi - - name: Download upstream AI review artifact env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -132,18 +89,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Fan out CryptoStrategies task issue - run: | - python3 scripts/fanout_monthly_optimization_tasks.py \ - --plan-file data/output/monthly_optimization/optimization_plan.json \ - --owner-repo CryptoStrategies \ - --repo "QuantStrategyLab/CryptoStrategies" \ - --planner-issue-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/issues/${{ steps.optimization_issue.outputs.issue_number }}" \ - --output-file data/output/monthly_optimization/fanout/crypto_strategies.json \ - --allow-permission-skip - env: - GITHUB_TOKEN: ${{ steps.cross_repo_token.outputs.token }} - - name: Append fanout summary run: | python3 - <<'PY' >> "$GITHUB_STEP_SUMMARY" diff --git a/README.md b/README.md index e0344ff..40226de 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,6 @@ CryptoSnapshotPipelines/ .github/ workflows/ monthly_publish.yml - ai_review.yml README.md requirements.txt .gitignore @@ -566,11 +565,11 @@ gh variable set SELFHOSTED_CODEX_REVIEW_PROVIDER --body auto gh secret set OPENAI_API_KEY --repo QuantStrategyLab/CryptoCodexAuditBridge --body "sk-..." ``` -The older `ai_review.yml` workflow is retained only as a manual compatibility path; normal automated AI review should be dispatched to `CryptoCodexAuditBridge`. +Source-local legacy AI review workflows are intentionally not kept in this repository. Provider fallback lives in `CryptoCodexAuditBridge`, so this source repository does not need Anthropic/OpenAI secrets. ### Codex Remediation and Auto-Merge Gate -The monthly optimization planner creates repo-scoped issues for follow-up tasks. For this repository, low-risk non-experiment tasks marked `[auto-pr-safe]` are labeled with: +The monthly optimization planner creates repo-scoped issues only for this snapshot repository. Low-risk non-experiment tasks marked `[auto-pr-safe]` are labeled with: - `monthly-optimization-task` - `codex-bridge` diff --git a/README.zh-CN.md b/README.zh-CN.md index abbe2aa..9f2d90c 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -92,7 +92,6 @@ CryptoSnapshotPipelines/ .github/ workflows/ monthly_publish.yml - ai_review.yml README.md README.zh-CN.md requirements.txt @@ -413,7 +412,7 @@ gh variable set SELFHOSTED_CODEX_REVIEW_PROVIDER --body auto gh secret set OPENAI_API_KEY --repo QuantStrategyLab/CryptoCodexAuditBridge --body "sk-..." ``` -旧 `ai_review.yml` workflow 只保留为手动兼容路径;正常自动 AI 审阅统一 dispatch 到 `CryptoCodexAuditBridge`。 +本仓库不再保留 source-local `ai_review.yml` 或 Claude 自动优化 workflow。provider fallback 统一放在 `CryptoCodexAuditBridge`,因此当前 source repo 不需要配置 Anthropic/OpenAI secrets。 ### Monthly Publish 的 GitHub 配置 diff --git a/docs/operator_runbook.md b/docs/operator_runbook.md index 488124e..d6a7461 100644 --- a/docs/operator_runbook.md +++ b/docs/operator_runbook.md @@ -78,9 +78,9 @@ Boundary rules: The monthly publish workflow creates a `monthly-review` issue, then dispatches `CryptoCodexAuditBridge` as the automated review and remediation path. The bridge owns provider selection through `SELFHOSTED_CODEX_REVIEW_PROVIDER`: `auto` is the default and runs the self-hosted Codex path first, falls back to OpenAI review when Codex setup or execution fails and the bridge has `OPENAI_API_KEY`, and fails loudly when the API fallback is not configured. `codex` disables API fallback; `openai` posts an API review comment only. -If the bridge dispatch fails, the monthly publish workflow fails loudly. The older source-local `ai_review.yml` workflow remains only as a manual compatibility path. +If the bridge dispatch fails, the monthly publish workflow fails loudly. Source-local legacy AI review workflows are intentionally removed; provider fallback lives in `CryptoCodexAuditBridge`. -The monthly optimization planner may still create repo-scoped follow-up issues after AI review. For `CryptoSnapshotPipelines`, low-risk non-experiment tasks marked `[auto-pr-safe]` are queued to the self-hosted VPS ccbot/Codex runner with the `codex-bridge` label. GitHub-hosted Claude Action remains only a manual-dispatch fallback; normal automated code remediation should run through ccbot/Codex. +The monthly optimization planner may still create repo-scoped follow-up issues after AI review, but only for `CryptoSnapshotPipelines`. Low-risk non-experiment tasks marked `[auto-pr-safe]` are queued to the self-hosted VPS ccbot/Codex runner with the `codex-bridge` label. Codex remediation PRs must use branch `codex/monthly-optimization-issue-`, include `` in the PR body, and start as draft. The auto-merge workflow only merges after CI passes, the PR is ready for review, `auto-merge-ok` is present, task-level auto-merge eligibility is recorded, and changed files stay outside guarded selector/config paths. diff --git a/scripts/build_monthly_optimization_plan.py b/scripts/build_monthly_optimization_plan.py index 9397275..88fa64f 100644 --- a/scripts/build_monthly_optimization_plan.py +++ b/scripts/build_monthly_optimization_plan.py @@ -10,7 +10,7 @@ RISK_ORDER = {"low": 0, "medium": 1, "high": 2} SCHEMA_VERSION = "2026-04-02" -REPO_ORDER = ["CryptoSnapshotPipelines", "CryptoStrategies"] +REPO_ORDER = ["CryptoSnapshotPipelines"] MANUAL_REVIEW_PREFIXES = ( "check ", "review ", diff --git a/tests/test_auto_optimization_pr_workflow_config.py b/tests/test_auto_optimization_pr_workflow_config.py index 1e1a6b4..87b370d 100644 --- a/tests/test_auto_optimization_pr_workflow_config.py +++ b/tests/test_auto_optimization_pr_workflow_config.py @@ -13,42 +13,15 @@ class AutoOptimizationPrWorkflowConfigTests(unittest.TestCase): def test_auto_optimization_workflow_handles_monthly_task_issues(self) -> None: - workflow = AUTO_WORKFLOW.read_text(encoding="utf-8") - - self.assertIn("issues:", workflow) - self.assertIn("monthly-optimization-task", workflow) - self.assertIn("!contains(github.event.issue.labels.*.name, 'codex-bridge')", workflow) - self.assertIn("workflow_dispatch:", workflow) - self.assertIn("issue_number:", workflow) - self.assertIn("actions: write", workflow) - self.assertIn("contents: write", workflow) - self.assertIn("pull-requests: write", workflow) - self.assertIn("ANTHROPIC_API_KEY", workflow) - self.assertIn("prepare_auto_optimization_pr.py", workflow) - self.assertIn("anthropics/claude-code-action@v1", workflow) - self.assertIn("Export selected task summary", workflow) - self.assertIn("claude_args: --max-turns 8", workflow) - self.assertIn("timeout-minutes: 15", workflow) - self.assertIn("steps.selected_tasks.outputs.task_summary", workflow) - self.assertIn("Do not use Bash in this workflow.", workflow) - self.assertIn("The workflow will run CI after the PR is created.", workflow) - self.assertIn("Evaluate merge guardrails", workflow) - self.assertIn("task_level_auto_merge_allowed", workflow) - self.assertIn("gh pr ready", workflow) - self.assertIn("steps.merge_guard.outputs.merge_ready", workflow) - self.assertIn("gh workflow run ci.yml", workflow) - self.assertIn("fetch-depth: 0", workflow) - self.assertIn('FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"', workflow) - self.assertIn("You are working inside CryptoSnapshotPipelines, the upstream selector repository.", workflow) - self.assertIn("Never edit files under src/ or config/ in this automation step.", workflow) + self.assertFalse(AUTO_WORKFLOW.exists()) def test_auto_merge_workflow_waits_for_ci_and_merges_only_safe_ready_prs(self) -> None: workflow = MERGE_WORKFLOW.read_text(encoding="utf-8") self.assertIn("workflow_run:", workflow) self.assertIn('workflows: ["CI"]', workflow) - self.assertIn("automation/monthly-optimization-issue-", workflow) self.assertIn("codex/monthly-optimization-issue-", workflow) + self.assertNotIn("automation/monthly-optimization-issue-", workflow) self.assertIn("gh pr view", workflow) self.assertIn("labels", workflow) self.assertIn("evaluate_changed_files", workflow) diff --git a/tests/test_build_monthly_optimization_plan.py b/tests/test_build_monthly_optimization_plan.py index 40ae4e5..ae06702 100644 --- a/tests/test_build_monthly_optimization_plan.py +++ b/tests/test_build_monthly_optimization_plan.py @@ -33,7 +33,7 @@ def test_build_plan_groups_in_scope_actions_by_owner_repo(self) -> None: self.assertEqual(len(plan["safe_auto_pr_candidates"]), 1) self.assertEqual(len(plan["experiment_candidates"]), 1) - def test_build_plan_keeps_downstream_actions_out_of_scope(self) -> None: + def test_build_plan_keeps_non_snapshot_actions_out_of_scope(self) -> None: upstream_review = { "source_repo": "QuantStrategyLab/CryptoSnapshotPipelines", "review_kind": "upstream_selector", @@ -63,10 +63,13 @@ def test_build_plan_keeps_downstream_actions_out_of_scope(self) -> None: plan = build_plan(upstream_review) + self.assertNotIn("CryptoStrategies", plan["repo_action_summary"]) self.assertNotIn("BinancePlatform", plan["repo_action_summary"]) - self.assertIn("CryptoStrategies", plan["repo_action_summary"]) - self.assertEqual(plan["out_of_scope_actions"][0]["owner_repo"], "BinancePlatform") - self.assertEqual(len(plan["safe_auto_pr_candidates"]), 1) + self.assertEqual( + sorted(action["owner_repo"] for action in plan["out_of_scope_actions"]), + ["BinancePlatform", "CryptoStrategies"], + ) + self.assertEqual(len(plan["safe_auto_pr_candidates"]), 0) def test_render_summary_markdown_mentions_source_reviews_and_repos(self) -> None: plan = { diff --git a/tests/test_monthly_optimization_planner_workflow_config.py b/tests/test_monthly_optimization_planner_workflow_config.py index dc0d56f..09d3f43 100644 --- a/tests/test_monthly_optimization_planner_workflow_config.py +++ b/tests/test_monthly_optimization_planner_workflow_config.py @@ -15,15 +15,6 @@ def test_planner_workflow_downloads_artifacts_posts_issue_and_fans_out_tasks(sel self.assertIn("workflow_dispatch:", workflow) self.assertIn("upstream_run_id:", workflow) self.assertIn("actions: write", workflow) - self.assertIn("CROSS_REPO_GITHUB_TOKEN", workflow) - self.assertIn("CROSS_REPO_GITHUB_APP_PRIVATE_KEY", workflow) - self.assertIn("CROSS_REPO_GITHUB_APP_ID", workflow) - self.assertIn("Detect GitHub App credentials", workflow) - self.assertIn("has_app_credentials", workflow) - self.assertIn("actions/create-github-app-token@v3", workflow) - self.assertIn("Resolve cross-repo access token", workflow) - self.assertIn("source=github_app", workflow) - self.assertIn("source=personal_access_token", workflow) self.assertIn("gh run download", workflow) self.assertIn("Resolve downloaded artifact paths", workflow) self.assertIn("Prepare upstream review payload", workflow) @@ -32,15 +23,16 @@ def test_planner_workflow_downloads_artifacts_posts_issue_and_fans_out_tasks(sel self.assertIn("post_monthly_optimization_issue.py", workflow) self.assertIn("fanout_monthly_optimization_tasks.py", workflow) self.assertIn("Fan out CryptoSnapshotPipelines task issue", workflow) - self.assertIn("Fan out CryptoStrategies task issue", workflow) self.assertIn("Resolve upstream experiment validation target", workflow) self.assertIn("Dispatch CryptoSnapshotPipelines experiment validation", workflow) self.assertIn("gh workflow run experiment_validation.yml", workflow) - self.assertIn("--allow-permission-skip", workflow) self.assertIn("Append fanout summary", workflow) self.assertIn("upstream_review_payload.json", workflow) self.assertIn("actions/upload-artifact@v7", workflow) self.assertIn("monthly-optimization-plan-", workflow) + self.assertNotIn("CryptoStrategies", workflow) + self.assertNotIn("CROSS_REPO_GITHUB_TOKEN", workflow) + self.assertNotIn("actions/create-github-app-token@v3", workflow) self.assertNotIn("BinancePlatform", workflow) self.assertNotIn("downstream_run_id", workflow) self.assertNotIn("downstream_repo", workflow) diff --git a/tests/test_monthly_publish_workflow_config.py b/tests/test_monthly_publish_workflow_config.py index 2297af0..b4591a0 100644 --- a/tests/test_monthly_publish_workflow_config.py +++ b/tests/test_monthly_publish_workflow_config.py @@ -6,7 +6,6 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1] WORKFLOW_PATH = PROJECT_ROOT / ".github" / "workflows" / "monthly_publish.yml" -AI_REVIEW_WORKFLOW_PATH = PROJECT_ROOT / ".github" / "workflows" / "ai_review.yml" README_ZH_PATH = PROJECT_ROOT / "README.zh-CN.md" @@ -61,32 +60,11 @@ def test_monthly_review_issue_creation_does_not_require_gh_cli(self) -> None: self.assertNotIn("LEGACY_API_REVIEW_ENABLED", workflow) self.assertNotIn("/actions/workflows/ai_review.yml/dispatches", workflow) - def test_ai_review_workflow_supports_dispatch_and_comment_posting(self) -> None: - workflow = AI_REVIEW_WORKFLOW_PATH.read_text(encoding="utf-8") + def test_source_local_legacy_ai_workflows_are_removed(self) -> None: + workflow_dir = PROJECT_ROOT / ".github" / "workflows" - self.assertIn("workflow_dispatch:", workflow) - self.assertIn("issue_number:", workflow) - self.assertIn("vars.LEGACY_AI_REVIEW_ENABLED == 'true'", workflow) - self.assertIn("id-token: write", workflow) - self.assertIn("Load review issue context", workflow) - self.assertIn("api.github.com/repos/{repo}/issues/{issue_number}", workflow) - self.assertIn("github_token: ${{ secrets.GITHUB_TOKEN }}", workflow) - self.assertIn('FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"', workflow) - self.assertIn("This is a strategy-optimization review, not only a release QA check.", workflow) - self.assertIn("If shadow or challenger tracks are missing, say evidence is incomplete", workflow) - self.assertIn("Strategy Optimization Directions", workflow) - self.assertIn("post_monthly_ai_review_comment.py", workflow) - self.assertIn("render_monthly_ai_review.py", workflow) - self.assertIn("run_openai_secondary_review.py", workflow) - self.assertIn("build_ai_review_payload.py", workflow) - self.assertIn("steps.claude_review.outputs.execution_file", workflow) - self.assertIn("OPENAI_API_KEY", workflow) - self.assertIn("OPENAI_SECONDARY_MODEL", workflow) - self.assertIn("secondary_review.json", workflow) - self.assertIn("final_review_payload.json", workflow) - self.assertIn("actions/upload-artifact@v7", workflow) - self.assertNotIn("allowed_tools:", workflow) - self.assertNotIn("custom_instructions:", workflow) + self.assertFalse((workflow_dir / "ai_review.yml").exists()) + self.assertFalse((workflow_dir / "auto_optimization_pr.yml").exists()) def test_chinese_readme_matches_current_monthly_review_defaults(self) -> None: readme = README_ZH_PATH.read_text(encoding="utf-8") @@ -96,6 +74,7 @@ def test_chinese_readme_matches_current_monthly_review_defaults(self) -> None: self.assertIn("OPENAI_API_KEY", readme) self.assertIn("配置在 `CryptoCodexAuditBridge`", readme) self.assertIn("必须从 GitHub variable 读取", readme) + self.assertIn("本仓库不再保留 source-local `ai_review.yml`", readme) self.assertNotIn("只配置 `ANTHROPIC_API_KEY`", readme) self.assertNotIn("如果这两个旧值还在 secret 里,也会继续兼容", readme)