From ed4ca871a3eefa244ba91c53a7dea489181a2b5f Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Thu, 2 Apr 2026 04:59:43 +0800 Subject: [PATCH] fix: tighten planner task routing --- scripts/build_monthly_optimization_plan.py | 85 ++++++++++++++++++- scripts/prepare_auto_optimization_pr.py | 10 ++- tests/test_build_monthly_optimization_plan.py | 48 +++++++++++ tests/test_prepare_auto_optimization_pr.py | 6 +- 4 files changed, 141 insertions(+), 8 deletions(-) diff --git a/scripts/build_monthly_optimization_plan.py b/scripts/build_monthly_optimization_plan.py index 1056685..140e908 100644 --- a/scripts/build_monthly_optimization_plan.py +++ b/scripts/build_monthly_optimization_plan.py @@ -11,6 +11,85 @@ RISK_ORDER = {"low": 0, "medium": 1, "high": 2} SCHEMA_VERSION = "2026-04-02" REPO_ORDER = ["CryptoLeaderRotation", "CryptoStrategies", "BinancePlatform"] +MANUAL_REVIEW_PREFIXES = ( + "check ", + "review ", + "audit ", + "reconcile ", + "assess ", + "confirm ", + "determine ", + "verify ", +) + + +def _combined_action_text(action: dict[str, Any]) -> str: + return f"{action.get('title', '')} {action.get('summary', '')}".lower() + + +def _resolve_owner_repo(source_review: dict[str, Any], action: dict[str, Any]) -> str: + text = _combined_action_text(action) + + if any( + marker in text + for marker in ( + "monthly report", + "cash-flow", + "cash flow", + "withdrawal", + "deposit", + "realized pnl", + "unrealized pnl", + "open positions", + "no-trade", + "gating", + "free usdt", + "dca", + "rotation", + ) + ): + return "BinancePlatform" + + if any( + marker in text + for marker in ( + "shadow build", + "challenger", + "tie-break", + "tie break", + "equal scores", + "boundary", + "near-cutoff", + "near-cutoff", + "selection threshold", + ) + ): + return "CryptoLeaderRotation" + + return action["owner_repo"] + + +def _resolve_auto_pr_safe(source_review: dict[str, Any], action: dict[str, Any]) -> bool: + title = str(action.get("title", "")).strip().lower() + summary = str(action.get("summary", "")).strip().lower() + if not bool(action.get("auto_pr_safe")): + return False + + if title.startswith(MANUAL_REVIEW_PREFIXES): + return False + if any( + phrase in summary + for phrase in ( + "assess whether", + "confirm whether", + "determine whether", + "verify minimum order size", + "review logs", + "pull binance transaction history", + ) + ): + return False + return True def highest_risk(actions: list[dict[str, Any]]) -> str: @@ -31,16 +110,18 @@ def sort_actions(actions: list[dict[str, Any]]) -> list[dict[str, Any]]: def normalize_action(source_review: dict[str, Any], action: dict[str, Any]) -> dict[str, Any]: + owner_repo = _resolve_owner_repo(source_review, action) + auto_pr_safe = _resolve_auto_pr_safe(source_review, action) return { "source_repo": source_review["source_repo"], "source_issue_number": source_review["source_issue"]["number"], "source_issue_title": source_review["source_issue"]["title"], "source_issue_url": source_review["source_issue"]["url"], "source_review_kind": source_review["review_kind"], - "owner_repo": action["owner_repo"], + "owner_repo": owner_repo, "title": action["title"], "risk_level": action["risk_level"], - "auto_pr_safe": bool(action.get("auto_pr_safe")), + "auto_pr_safe": auto_pr_safe, "experiment_only": bool(action.get("experiment_only")), "summary": action["summary"], } diff --git a/scripts/prepare_auto_optimization_pr.py b/scripts/prepare_auto_optimization_pr.py index d3fa7aa..6a1ec44 100644 --- a/scripts/prepare_auto_optimization_pr.py +++ b/scripts/prepare_auto_optimization_pr.py @@ -68,10 +68,10 @@ def _is_completed_low_risk_task(action: dict[str, Any], repo_root: Path) -> bool repo_name = repo_root.name if repo_name == "CryptoLeaderRotation": - if "shadow/challenger build generation" in title: + if "shadow/challenger build generation" in title or "shadow build" in title: workflow = _read_text(repo_root / ".github" / "workflows" / "monthly_publish.yml") return "run_monthly_shadow_build.py" in workflow - if "deterministic tie-break behavior" in title: + if "tie-break" in title or "tie break" in title: readme = _read_text(repo_root / "README.md") runbook = _read_text(repo_root / "docs" / "operator_runbook.md") return ( @@ -79,7 +79,11 @@ def _is_completed_low_risk_task(action: dict[str, Any], repo_root: Path) -> bool and "deterministic tie-break" in runbook ) - if repo_name == "BinancePlatform" and "zero-trade diagnostics" in title: + if repo_name == "BinancePlatform" and ( + "zero-trade diagnostics" in title + or "diagnostic reporting for no-trade months" in title + or "no-trade months" in title + ): monthly_report = _read_text(repo_root / "scripts" / "run_monthly_report_bundle.py") return ( "No explicit gating or no-trade reasons were recorded this month." in monthly_report diff --git a/tests/test_build_monthly_optimization_plan.py b/tests/test_build_monthly_optimization_plan.py index 5148cf0..badeadf 100644 --- a/tests/test_build_monthly_optimization_plan.py +++ b/tests/test_build_monthly_optimization_plan.py @@ -52,6 +52,54 @@ def test_build_plan_groups_actions_by_owner_repo(self) -> None: self.assertEqual(len(plan["safe_auto_pr_candidates"]), 2) self.assertEqual(len(plan["experiment_candidates"]), 1) + def test_build_plan_reassigns_reporting_tasks_and_downgrades_manual_checks(self) -> None: + upstream_review = { + "source_repo": "QuantStrategyLab/CryptoLeaderRotation", + "review_kind": "upstream_selector", + "source_issue": {"number": 11, "title": "Monthly Report Review: 2026-04-01", "url": "https://github.com/a/b/issues/11"}, + "risk_level": "low", + "production_recommendation": "keep_production_as_is", + "summary": "Upstream is stable.", + "recommended_actions": [], + } + downstream_review = { + "source_repo": "QuantStrategyLab/BinancePlatform", + "review_kind": "execution_runtime", + "source_issue": {"number": 9, "title": "Monthly Execution Review: 2026-03", "url": "https://github.com/a/b/issues/9"}, + "risk_level": "medium", + "production_recommendation": "needs_attention", + "summary": "Execution needs follow-up.", + "recommended_actions": [ + { + "owner_repo": "CryptoStrategies", + "title": "Add monthly report cash-flow attribution", + "risk_level": "low", + "auto_pr_safe": True, + "experiment_only": False, + "summary": "Extend the monthly report to show deposits, withdrawals, realized PnL, and unrealized PnL separately.", + }, + { + "owner_repo": "BinancePlatform", + "title": "Check DCA and rotation eligibility gates against current free USDT", + "risk_level": "low", + "auto_pr_safe": True, + "experiment_only": False, + "summary": "Verify minimum order size, reserve floor, and available balance thresholds.", + }, + ], + } + + plan = build_plan(upstream_review, downstream_review) + + bp_actions = plan["repo_action_summary"]["BinancePlatform"]["actions"] + self.assertEqual([action["title"] for action in bp_actions], [ + "Check DCA and rotation eligibility gates against current free USDT", + "Add monthly report cash-flow attribution", + ]) + self.assertEqual(bp_actions[0]["auto_pr_safe"], False) + self.assertEqual(bp_actions[1]["auto_pr_safe"], True) + self.assertEqual(len(plan["safe_auto_pr_candidates"]), 1) + def test_render_summary_markdown_mentions_source_reviews_and_repos(self) -> None: plan = { "highest_review_risk": "medium", diff --git a/tests/test_prepare_auto_optimization_pr.py b/tests/test_prepare_auto_optimization_pr.py index fc9934a..8ed9a34 100644 --- a/tests/test_prepare_auto_optimization_pr.py +++ b/tests/test_prepare_auto_optimization_pr.py @@ -21,8 +21,8 @@ def setUp(self) -> None: - [ ] `low` Restore monthly shadow/challenger build generation before review [auto-pr-safe] - Summary: Ensure `official_baseline` and `challenger_topk_60` are produced each month. - Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11) -- [ ] `low` Document deterministic tie-break behavior for equal-score boundary cases [auto-pr-safe] - - Summary: Add explicit documentation or report wording describing how equal scores are ordered and selected. +- [ ] `low` Document and verify tie-breaking for equal scores [auto-pr-safe] + - Summary: Confirm the secondary sort used for equal scores is deterministic, stable, and documented. - Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11) - [ ] `low` Add a boundary tracker [auto-pr-safe, experiment-only] - Summary: Track near-cutoff symbols monthly. @@ -49,7 +49,7 @@ def test_build_payload_skips_completed_clr_tasks_and_excludes_experiments(self) [action["title"] for action in payload["skipped_actions"]], [ "Restore monthly shadow/challenger build generation before review", - "Document deterministic tie-break behavior for equal-score boundary cases", + "Document and verify tie-breaking for equal scores", ], )