diff --git a/.github/workflows/monthly_publish.yml b/.github/workflows/monthly_publish.yml index 1c147bd..f776ff7 100644 --- a/.github/workflows/monthly_publish.yml +++ b/.github/workflows/monthly_publish.yml @@ -31,8 +31,8 @@ jobs: SELFHOSTED_CODEX_REVIEW_ENABLED: ${{ vars.SELFHOSTED_CODEX_REVIEW_ENABLED || 'true' }} SELFHOSTED_CODEX_REVIEW_REPOSITORY: ${{ vars.SELFHOSTED_CODEX_REVIEW_REPOSITORY || 'QuantStrategyLab/CryptoCodexAuditBridge' }} SELFHOSTED_CODEX_REVIEW_MODE: ${{ vars.SELFHOSTED_CODEX_REVIEW_MODE || 'review_and_fix' }} + SELFHOSTED_CODEX_REVIEW_PROVIDER: ${{ vars.SELFHOSTED_CODEX_REVIEW_PROVIDER || 'codex' }} SELFHOSTED_CODEX_REVIEW_AUTO_MERGE: ${{ vars.SELFHOSTED_CODEX_REVIEW_AUTO_MERGE || 'false' }} - LEGACY_AI_REVIEW_ENABLED: ${{ vars.LEGACY_AI_REVIEW_ENABLED || 'false' }} steps: - name: Checkout @@ -202,8 +202,6 @@ jobs: if: success() && env.PUBLISH_ENABLED != 'false' env: APP_TOKEN: ${{ steps.codex_review_app_token.outputs.token }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} CODEX_AUDIT_DISPATCH_TOKEN: ${{ secrets.CODEX_AUDIT_DISPATCH_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: ${{ github.repository }} @@ -212,9 +210,9 @@ jobs: ISSUE_URL: ${{ steps.review_issue.outputs.issue_url }} TARGET_REPOSITORY: ${{ env.SELFHOSTED_CODEX_REVIEW_REPOSITORY }} REVIEW_MODE: ${{ env.SELFHOSTED_CODEX_REVIEW_MODE }} + REVIEW_PROVIDER: ${{ env.SELFHOSTED_CODEX_REVIEW_PROVIDER }} AUTO_MERGE: ${{ env.SELFHOSTED_CODEX_REVIEW_AUTO_MERGE }} CODEX_REVIEW_ENABLED: ${{ env.SELFHOSTED_CODEX_REVIEW_ENABLED }} - LEGACY_API_REVIEW_ENABLED: ${{ env.LEGACY_AI_REVIEW_ENABLED }} run: | set -euo pipefail python - <<'PY' @@ -255,6 +253,9 @@ jobs: mode = os.environ["REVIEW_MODE"].strip() or "review_and_fix" if mode not in {"review_only", "review_and_fix"}: raise RuntimeError(f"Unsupported Codex review mode: {mode}") + provider = os.environ["REVIEW_PROVIDER"].strip() or "codex" + if provider not in {"codex", "openai", "auto"}: + raise RuntimeError(f"Unsupported Codex review provider: {provider}") payload = { "ref": "main", "inputs": { @@ -262,6 +263,7 @@ jobs: "source_ref": os.environ["GITHUB_REF_NAME"], "issue_number": os.environ["ISSUE_NUMBER"], "mode": mode, + "provider": provider, "auto_merge": str(os.environ["AUTO_MERGE"].strip().lower() == "true").lower(), }, } @@ -277,50 +279,11 @@ jobs: f"to {target_repository}" ) - def dispatch_legacy_api() -> None: - if not os.environ.get("ANTHROPIC_API_KEY", "").strip(): - raise RuntimeError("Legacy API review fallback is enabled but ANTHROPIC_API_KEY is not configured") - if not os.environ.get("OPENAI_API_KEY", "").strip(): - raise RuntimeError("Legacy API review fallback is enabled but OPENAI_API_KEY is not configured") - repository = os.environ["GITHUB_REPOSITORY"] - ref_name = os.environ["GITHUB_REF_NAME"] - issue_number = os.environ["ISSUE_NUMBER"] - payload = {"ref": ref_name, "inputs": {"issue_number": issue_number}} - status = dispatch( - os.environ["GITHUB_TOKEN"], - f"https://api.github.com/repos/{repository}/actions/workflows/ai_review.yml/dispatches", - payload, - ) - if status not in (201, 204): - raise RuntimeError(f"Unexpected legacy AI dispatch status: {status}") - print(f"Dispatched legacy API AI monthly review for issue #{issue_number} on ref {ref_name}") - - codex_error = "" if enabled("CODEX_REVIEW_ENABLED"): - try: - dispatch_codex() - raise SystemExit(0) - except (RuntimeError, urllib.error.URLError, urllib.error.HTTPError) as exc: - codex_error = str(exc) - print(f"Codex monthly review dispatch failed: {codex_error}") - else: - codex_error = "Codex monthly review is disabled" - print(codex_error) - - if enabled("LEGACY_API_REVIEW_ENABLED"): - try: - dispatch_legacy_api() - raise SystemExit(0) - except (RuntimeError, urllib.error.URLError, urllib.error.HTTPError) as exc: - raise RuntimeError( - f"Codex monthly review failed ({codex_error}); legacy API review fallback also failed: {exc}" - ) from exc + dispatch_codex() + raise SystemExit(0) - raise RuntimeError( - "Codex monthly review failed or was disabled, and legacy API fallback is disabled. " - "Set LEGACY_AI_REVIEW_ENABLED=true and configure ANTHROPIC_API_KEY plus OPENAI_API_KEY " - f"to enable the API fallback. Codex failure: {codex_error}" - ) + raise RuntimeError("Monthly review bridge dispatch is disabled by SELFHOSTED_CODEX_REVIEW_ENABLED=false") PY - name: Write Release Heartbeat diff --git a/README.md b/README.md index f7b7563..29198a1 100644 --- a/README.md +++ b/README.md @@ -533,9 +533,13 @@ Behavior: ## Automated AI Monthly Review -After the monthly report bundle is assembled, the workflow creates a GitHub Issue containing the full `ai_review_input.md` content. The default automated review route dispatches `QuantStrategyLab/CryptoCodexAuditBridge`, which runs Codex on a self-hosted VPS runner. Codex reads the monthly issue, posts the audit result back to the issue, and opens a PR directly for safe low-risk fixes. +After the monthly report bundle is assembled, the workflow creates a GitHub Issue containing the full `ai_review_input.md` content. The automated review route dispatches `QuantStrategyLab/CryptoCodexAuditBridge`. The bridge owns provider selection through `SELFHOSTED_CODEX_REVIEW_PROVIDER`: -The legacy API-based dual AI review remains available as a compatibility fallback. Set `LEGACY_AI_REVIEW_ENABLED=true` and configure both `ANTHROPIC_API_KEY` and `OPENAI_API_KEY` to allow the monthly workflow to dispatch `ai_review.yml` if the Codex bridge dispatch fails. When Codex dispatch fails and the legacy API fallback is not enabled or not configured, the monthly publish workflow fails loudly instead of silently skipping review. +- `codex` (default): run Codex on the self-hosted VPS runner, post the audit result, and open a PR directly for safe low-risk fixes. +- `openai`: run an API review inside the bridge and post a review comment only. +- `auto`: try Codex first; if Codex fails and the bridge has `OPENAI_API_KEY`, post the API review fallback from the bridge. + +If the bridge dispatch itself fails, the monthly publish workflow fails loudly instead of silently skipping review. The AI review covers: @@ -545,24 +549,24 @@ The AI review covers: - **Operator action items**: summarizes the checklist and adds any AI-identified follow-up items - **Code improvements**: Codex can open focused PRs directly for low-risk reporting, validation, workflow, test, or documentation defects; sensitive selector changes remain manual-review work -Review output is posted back to the monthly issue. The legacy API workflow still renders bilingual output when enabled. +Review output is posted back to the monthly issue. -### Optional Legacy API Fallback Secrets +### Optional Bridge API Fallback -- `ANTHROPIC_API_KEY`: Anthropic API key for Claude Code Action -- `OPENAI_API_KEY`: OpenAI API key for the secondary monthly review +- `SELFHOSTED_CODEX_REVIEW_PROVIDER`: set to `openai` or `auto` in this source repository. +- `OPENAI_API_KEY`: configure in `CryptoCodexAuditBridge`, not this source repository. +- `OPENAI_MODEL`: optional bridge repository variable, default `gpt-5.4-mini`. -The default production configuration does not need these API secrets because it uses `CryptoCodexAuditBridge`. Configure them only if you want the legacy dual AI fallback. +The default production configuration does not need model API secrets because it uses Codex through `CryptoCodexAuditBridge`. Setup: ```bash -gh variable set LEGACY_AI_REVIEW_ENABLED --body true -gh secret set ANTHROPIC_API_KEY --body "sk-ant-..." -gh secret set OPENAI_API_KEY --body "sk-..." +gh variable set SELFHOSTED_CODEX_REVIEW_PROVIDER --body auto +gh secret set OPENAI_API_KEY --repo QuantStrategyLab/CryptoCodexAuditBridge --body "sk-..." ``` -The legacy AI review workflow runs on `ubuntu-latest` (no self-hosted runner required) and costs approximately $0.01-0.05 per monthly run. It is retained for open-source compatibility and emergency fallback, not as the normal production path. +The older `ai_review.yml` workflow is retained only as a manual compatibility path; normal automated AI review should be dispatched to `CryptoCodexAuditBridge`. ### Codex Remediation and Auto-Merge Gate @@ -1023,7 +1027,7 @@ Practical review file selection: Automated AI handoff: -The workflow automatically creates a GitHub Issue with the `monthly-review` label, then dispatches `CryptoCodexAuditBridge`. If the Codex bridge dispatch fails and `LEGACY_AI_REVIEW_ENABLED=true` with both API secrets configured, the workflow falls back to the legacy dual AI `ai_review.yml`; otherwise it fails loudly. See the "Automated AI Monthly Review" section for details. +The workflow automatically creates a GitHub Issue with the `monthly-review` label, then dispatches `CryptoCodexAuditBridge`. Provider fallback is handled inside the bridge through `SELFHOSTED_CODEX_REVIEW_PROVIDER`; if the bridge dispatch fails, the workflow fails loudly. See the "Automated AI Monthly Review" section for details. Manual AI handoff (fallback): diff --git a/README.zh-CN.md b/README.zh-CN.md index fb2c4b4..baaa8b8 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -380,9 +380,13 @@ make monthly-review-briefing ## 自动化 AI 月度审阅 -月报 bundle 组装完成后,workflow 会自动创建一个 GitHub Issue,内容为完整的 `ai_review_input.md`。默认自动审阅路径会 dispatch `QuantStrategyLab/CryptoCodexAuditBridge`,由 self-hosted VPS runner 上已登录的 Codex CLI 读取月报 Issue、回帖审计结果,并在发现安全、低风险的问题时直接创建修复 PR。 +月报 bundle 组装完成后,workflow 会自动创建一个 GitHub Issue,内容为完整的 `ai_review_input.md`。自动审阅路径会 dispatch `QuantStrategyLab/CryptoCodexAuditBridge`,由 bridge 统一决定 provider: -旧的 API 双 AI 审阅仍保留为兼容性 fallback。只有在设置 `LEGACY_AI_REVIEW_ENABLED=true`,并同时配置 `ANTHROPIC_API_KEY` 与 `OPENAI_API_KEY` 时,月度 workflow 才会在 Codex bridge dispatch 失败后调用 `ai_review.yml`。如果 Codex dispatch 失败且 legacy API fallback 未启用或缺少凭据,monthly publish workflow 会直接失败,而不是静默跳过审阅。 +- `codex`(默认):由 self-hosted VPS runner 上已登录的 Codex CLI 读取月报 Issue、回帖审计结果,并在发现安全、低风险的问题时直接创建修复 PR。 +- `openai`:在 bridge 内运行 API 审阅,只回帖,不改代码。 +- `auto`:先跑 Codex;如果 Codex 失败且 bridge 配置了 `OPENAI_API_KEY`,由 bridge 回落到 OpenAI API 审阅。 + +如果 bridge dispatch 本身失败,monthly publish workflow 会直接失败,而不是静默跳过审阅。 AI 审阅覆盖范围: @@ -392,24 +396,24 @@ AI 审阅覆盖范围: - **操作员待办事项**:汇总 checklist 并补充 AI 识别出的跟进事项 - **代码改进**:Codex 可以为低风险的 reporting、validation、workflow、test 或 documentation 问题直接创建聚焦 PR;涉及 selector、threshold、universe 或交易行为的变更仍需人工决策 -审阅结果会回帖到月度 Issue。legacy API workflow 在启用时仍会渲染中英文输出。 +审阅结果会回帖到月度 Issue。 -### 可选 Legacy API Fallback Secrets +### 可选 Bridge API Fallback -- `ANTHROPIC_API_KEY`:Claude Code Action 使用的 Anthropic API key -- `OPENAI_API_KEY`:二次月度审阅使用的 OpenAI API key +- `SELFHOSTED_CODEX_REVIEW_PROVIDER`:在当前 source repo 设置为 `openai` 或 `auto`。 +- `OPENAI_API_KEY`:配置在 `CryptoCodexAuditBridge`,不要配置在当前 source repo。 +- `OPENAI_MODEL`:可选 bridge repo variable,默认 `gpt-5.4-mini`。 -默认生产配置不需要这些 API secrets,因为默认使用 `CryptoCodexAuditBridge`。只有需要启用 legacy 双 AI fallback 时才配置它们。 +默认生产配置不需要模型 API secrets,因为默认使用 `CryptoCodexAuditBridge` 的 Codex provider。 配置方式示例: ```bash -gh variable set LEGACY_AI_REVIEW_ENABLED --body true -gh secret set ANTHROPIC_API_KEY --body "sk-ant-..." -gh secret set OPENAI_API_KEY --body "sk-..." +gh variable set SELFHOSTED_CODEX_REVIEW_PROVIDER --body auto +gh secret set OPENAI_API_KEY --repo QuantStrategyLab/CryptoCodexAuditBridge --body "sk-..." ``` -legacy AI review workflow 运行在 `ubuntu-latest`(不需要 self-hosted runner),每月运行一次费用约 $0.01-0.05。它保留用于开源兼容和应急 fallback,不是当前默认生产路径。 +旧 `ai_review.yml` workflow 只保留为手动兼容路径;正常自动 AI 审阅统一 dispatch 到 `CryptoCodexAuditBridge`。 ### Monthly Publish 的 GitHub 配置 diff --git a/docs/operator_runbook.md b/docs/operator_runbook.md index d8dd020..21ed8ec 100644 --- a/docs/operator_runbook.md +++ b/docs/operator_runbook.md @@ -76,9 +76,9 @@ Boundary rules: ## Monthly Codex Remediation -The monthly publish workflow creates a `monthly-review` issue, then dispatches `CryptoCodexAuditBridge` as the primary automated review and remediation path. Codex posts its audit result back to the issue and may open a focused PR directly for low-risk reporting, validation, workflow, test, or documentation fixes. +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`: `codex` runs self-hosted Codex and may open focused PRs, `openai` posts an API review comment only, and `auto` falls back to OpenAI review when Codex fails and the bridge has `OPENAI_API_KEY`. -The legacy Claude/OpenAI dual review workflow remains only as a compatibility fallback. Enable it with `LEGACY_AI_REVIEW_ENABLED=true` and configure both `ANTHROPIC_API_KEY` and `OPENAI_API_KEY`. If Codex dispatch fails and the legacy API fallback is disabled or missing credentials, the monthly publish workflow fails loudly. +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. 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. diff --git a/tests/test_monthly_publish_workflow_config.py b/tests/test_monthly_publish_workflow_config.py index 69cd07e..ad86134 100644 --- a/tests/test_monthly_publish_workflow_config.py +++ b/tests/test_monthly_publish_workflow_config.py @@ -48,16 +48,17 @@ def test_monthly_review_issue_creation_does_not_require_gh_cli(self) -> None: self.assertIn("APP_TOKEN", workflow) self.assertIn("Trigger Monthly Review Automation", workflow) self.assertIn("CODEX_AUDIT_DISPATCH_TOKEN", workflow) - self.assertIn("ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}", workflow) - self.assertIn("OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}", workflow) - self.assertIn("Codex monthly review dispatch failed", workflow) - self.assertIn("legacy API review fallback", workflow) - self.assertIn("legacy API fallback is disabled", workflow) + self.assertIn("SELFHOSTED_CODEX_REVIEW_PROVIDER", workflow) + self.assertIn("REVIEW_PROVIDER", workflow) + self.assertIn('"provider": provider', workflow) + self.assertNotIn("ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}", workflow) + self.assertNotIn("OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}", workflow) + self.assertNotIn("legacy API review fallback", workflow) self.assertIn("selfhosted_monthly_review.yml", workflow) self.assertIn("/actions/workflows/selfhosted_monthly_review.yml/dispatches", workflow) self.assertNotIn("/repos/{target_repository}/dispatches", workflow) - self.assertIn("LEGACY_API_REVIEW_ENABLED", workflow) - self.assertIn("/actions/workflows/ai_review.yml/dispatches", workflow) + 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") @@ -90,9 +91,9 @@ def test_chinese_readme_matches_current_monthly_review_defaults(self) -> None: readme = README_ZH_PATH.read_text(encoding="utf-8") self.assertIn("CryptoCodexAuditBridge", readme) - self.assertIn("LEGACY_AI_REVIEW_ENABLED=true", readme) + self.assertIn("SELFHOSTED_CODEX_REVIEW_PROVIDER", readme) self.assertIn("OPENAI_API_KEY", readme) - self.assertIn("不需要这些 API secrets", readme) + self.assertIn("配置在 `CryptoCodexAuditBridge`", readme) self.assertIn("必须从 GitHub variable 读取", readme) self.assertNotIn("只配置 `ANTHROPIC_API_KEY`", readme) self.assertNotIn("如果这两个旧值还在 secret 里,也会继续兼容", readme)