Skip to content

Commit fed2dd7

Browse files
committed
fix: skip stale auto optimization tasks
1 parent 83f0f13 commit fed2dd7

2 files changed

Lines changed: 77 additions & 17 deletions

File tree

scripts/prepare_auto_optimization_pr.py

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
SUMMARY_RE = re.compile(r"^\s+- Summary: (?P<summary>.+)$")
1212
SOURCE_RE = re.compile(r"^\s+- Source: \[(?P<label>.+?)\]\((?P<url>[^)]+)\)$")
1313
MARKER_PREFIX = "<!-- auto-optimization-pr:issue-"
14+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
1415

1516

1617
def parse_actions(issue_body: str) -> list[dict[str, Any]]:
@@ -58,15 +59,56 @@ def parse_actions(issue_body: str) -> list[dict[str, Any]]:
5859
return actions
5960

6061

61-
def build_payload(issue_context: dict[str, Any]) -> dict[str, Any]:
62+
def _read_text(path: Path) -> str:
63+
return path.read_text(encoding="utf-8") if path.exists() else ""
64+
65+
66+
def _is_completed_low_risk_task(action: dict[str, Any], repo_root: Path) -> bool:
67+
title = str(action.get("title", "")).lower()
68+
repo_name = repo_root.name
69+
70+
if repo_name == "CryptoLeaderRotation":
71+
if "shadow/challenger build generation" in title:
72+
workflow = _read_text(repo_root / ".github" / "workflows" / "monthly_publish.yml")
73+
return "run_monthly_shadow_build.py" in workflow
74+
if "deterministic tie-break behavior" in title:
75+
readme = _read_text(repo_root / "README.md")
76+
runbook = _read_text(repo_root / "docs" / "operator_runbook.md")
77+
return (
78+
"Monthly ranking tie-break rule for `core_major` live exports:" in readme
79+
and "deterministic tie-break" in runbook
80+
)
81+
82+
if repo_name == "BinancePlatform" and "zero-trade diagnostics" in title:
83+
monthly_report = _read_text(repo_root / "scripts" / "run_monthly_report_bundle.py")
84+
return (
85+
"No explicit gating or no-trade reasons were recorded this month." in monthly_report
86+
and "gating_summary" in monthly_report
87+
)
88+
89+
return False
90+
91+
92+
def build_payload(issue_context: dict[str, Any], repo_root: Path | None = None) -> dict[str, Any]:
93+
repo_root = repo_root or PROJECT_ROOT
6294
issue_number = int(issue_context["number"])
6395
issue_title = str(issue_context["title"]).strip()
6496
issue_body = str(issue_context["body"])
6597
parsed_actions = parse_actions(issue_body)
66-
safe_actions = [
67-
action for action in parsed_actions
68-
if action["risk_level"] == "low" and "auto-pr-safe" in action.get("flags", [])
98+
low_safe_actions = [
99+
action
100+
for action in parsed_actions
101+
if action["risk_level"] == "low"
102+
and "auto-pr-safe" in action.get("flags", [])
103+
and "experiment-only" not in action.get("flags", [])
69104
]
105+
safe_actions: list[dict[str, Any]] = []
106+
skipped_actions: list[dict[str, Any]] = []
107+
for action in low_safe_actions:
108+
if _is_completed_low_risk_task(action, repo_root):
109+
skipped_actions.append({**action, "skip_reason": "already_implemented"})
110+
else:
111+
safe_actions.append(action)
70112
return {
71113
"issue_number": issue_number,
72114
"issue_title": issue_title,
@@ -75,7 +117,9 @@ def build_payload(issue_context: dict[str, Any]) -> dict[str, Any]:
75117
"pr_title": f"Draft: address monthly optimization issue #{issue_number}",
76118
"should_run": bool(safe_actions),
77119
"safe_task_count": len(safe_actions),
120+
"skipped_task_count": len(skipped_actions),
78121
"safe_actions": safe_actions,
122+
"skipped_actions": skipped_actions,
79123
}
80124

81125

@@ -86,8 +130,19 @@ def render_task_summary(payload: dict[str, Any]) -> str:
86130
f"- Issue: #{payload['issue_number']} {payload['issue_title']}",
87131
f"- Eligible low-risk auto-pr-safe tasks: `{payload['safe_task_count']}`",
88132
]
133+
if payload["skipped_actions"]:
134+
lines.append(f"- Skipped as already implemented: `{payload['skipped_task_count']}`")
89135
if not payload["safe_actions"]:
90-
lines.extend(["", "No eligible low-risk [auto-pr-safe] tasks were found in this issue."])
136+
lines.extend(["", "No eligible low-risk [auto-pr-safe] tasks remain for draft PR generation."])
137+
if payload["skipped_actions"]:
138+
lines.extend(["", "## Skipped Tasks"])
139+
for action in payload["skipped_actions"]:
140+
lines.extend(
141+
[
142+
f"- `{action['risk_level']}` {action['title']}",
143+
f" - Reason: {action['skip_reason']}",
144+
]
145+
)
91146
return "\n".join(lines).strip() + "\n"
92147

93148
lines.extend(["", "## Selected Tasks"])

tests/test_prepare_auto_optimization_pr.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3+
import tempfile
34
import unittest
5+
from pathlib import Path
46

57
from scripts.prepare_auto_optimization_pr import build_payload, parse_actions, render_pr_body
68

@@ -9,19 +11,19 @@ class PrepareAutoOptimizationPrTests(unittest.TestCase):
911
def setUp(self) -> None:
1012
self.issue_context = {
1113
"number": 15,
12-
"title": "Monthly Optimization Tasks · BinancePlatform: 2026-04-01 / 2026-03",
13-
"body": """# Monthly Optimization Tasks · BinancePlatform
14+
"title": "Monthly Optimization Tasks · Sandbox",
15+
"body": """# Monthly Optimization Tasks · Sandbox
1416
1517
## Actions
16-
- [ ] `high` Reconcile March cash flows and open-position state
17-
- Summary: Pull Binance transaction history for March.
18-
- Source: [QuantStrategyLab/BinancePlatform #9](https://github.com/QuantStrategyLab/BinancePlatform/issues/9)
18+
- [ ] `high` Investigate an upstream issue
19+
- Summary: Manual follow-up only.
20+
- Source: [Sandbox #0](https://example.com/issues/0)
1921
- [ ] `low` Add zero-trade diagnostics to the report [auto-pr-safe]
2022
- Summary: Include the top failed gating reason counts.
21-
- Source: [QuantStrategyLab/BinancePlatform #9](https://github.com/QuantStrategyLab/BinancePlatform/issues/9)
23+
- Source: [Sandbox #1](https://example.com/issues/1)
2224
- [ ] `low` Add a boundary tracker [auto-pr-safe, experiment-only]
2325
- Summary: Track near-cutoff symbols monthly.
24-
- Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11)
26+
- Source: [Sandbox #2](https://example.com/issues/2)
2527
""",
2628
}
2729

@@ -32,18 +34,21 @@ def test_parse_actions_preserves_risk_flags_and_source(self) -> None:
3234
self.assertEqual(actions[0]["risk_level"], "high")
3335
self.assertEqual(actions[1]["flags"], ["auto-pr-safe"])
3436
self.assertEqual(actions[2]["flags"], ["auto-pr-safe", "experiment-only"])
35-
self.assertEqual(actions[2]["source_label"], "QuantStrategyLab/CryptoLeaderRotation #11")
37+
self.assertEqual(actions[2]["source_label"], "Sandbox #2")
3638

37-
def test_build_payload_selects_only_low_auto_pr_safe_actions(self) -> None:
38-
payload = build_payload(self.issue_context)
39+
def test_build_payload_selects_only_non_experiment_low_auto_pr_safe_actions(self) -> None:
40+
with tempfile.TemporaryDirectory() as temp_dir:
41+
payload = build_payload(self.issue_context, repo_root=Path(temp_dir))
3942

4043
self.assertTrue(payload["should_run"])
41-
self.assertEqual(payload["safe_task_count"], 2)
44+
self.assertEqual(payload["safe_task_count"], 1)
45+
self.assertEqual(payload["skipped_task_count"], 0)
4246
self.assertEqual(payload["branch_name"], "automation/monthly-optimization-issue-15")
4347
self.assertEqual(payload["safe_actions"][0]["title"], "Add zero-trade diagnostics to the report")
4448

4549
def test_render_pr_body_contains_marker_and_issue_reference(self) -> None:
46-
payload = build_payload(self.issue_context)
50+
with tempfile.TemporaryDirectory() as temp_dir:
51+
payload = build_payload(self.issue_context, repo_root=Path(temp_dir))
4752
body = render_pr_body(payload)
4853

4954
self.assertIn("<!-- auto-optimization-pr:issue-15 -->", body)

0 commit comments

Comments
 (0)