Skip to content

Commit fa382ad

Browse files
committed
fix: skip stale auto optimization tasks
1 parent 5f7b605 commit fa382ad

2 files changed

Lines changed: 104 additions & 26 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: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
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

79

10+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
11+
12+
813
class PrepareAutoOptimizationPrTests(unittest.TestCase):
914
def setUp(self) -> None:
1015
self.issue_context = {
11-
"number": 15,
12-
"title": "Monthly Optimization Tasks · BinancePlatform: 2026-04-01 / 2026-03",
13-
"body": """# Monthly Optimization Tasks · BinancePlatform
16+
"number": 22,
17+
"title": "Monthly Optimization Tasks · CryptoLeaderRotation: 2026-04-01 / 2026-03",
18+
"body": """# Monthly Optimization Tasks · CryptoLeaderRotation
1419
1520
## 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)
19-
- [ ] `low` Add zero-trade diagnostics to the report [auto-pr-safe]
20-
- Summary: Include the top failed gating reason counts.
21-
- Source: [QuantStrategyLab/BinancePlatform #9](https://github.com/QuantStrategyLab/BinancePlatform/issues/9)
21+
- [ ] `low` Restore monthly shadow/challenger build generation before review [auto-pr-safe]
22+
- Summary: Ensure `official_baseline` and `challenger_topk_60` are produced each month.
23+
- Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11)
24+
- [ ] `low` Document deterministic tie-break behavior for equal-score boundary cases [auto-pr-safe]
25+
- Summary: Add explicit documentation or report wording describing how equal scores are ordered and selected.
26+
- Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11)
2227
- [ ] `low` Add a boundary tracker [auto-pr-safe, experiment-only]
2328
- Summary: Track near-cutoff symbols monthly.
2429
- Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11)
@@ -29,26 +34,44 @@ def test_parse_actions_preserves_risk_flags_and_source(self) -> None:
2934
actions = parse_actions(self.issue_context["body"])
3035

3136
self.assertEqual(len(actions), 3)
32-
self.assertEqual(actions[0]["risk_level"], "high")
33-
self.assertEqual(actions[1]["flags"], ["auto-pr-safe"])
37+
self.assertEqual(actions[0]["risk_level"], "low")
38+
self.assertEqual(actions[0]["flags"], ["auto-pr-safe"])
3439
self.assertEqual(actions[2]["flags"], ["auto-pr-safe", "experiment-only"])
3540
self.assertEqual(actions[2]["source_label"], "QuantStrategyLab/CryptoLeaderRotation #11")
3641

37-
def test_build_payload_selects_only_low_auto_pr_safe_actions(self) -> None:
38-
payload = build_payload(self.issue_context)
42+
def test_build_payload_skips_completed_clr_tasks_and_excludes_experiments(self) -> None:
43+
payload = build_payload(self.issue_context, repo_root=PROJECT_ROOT)
3944

40-
self.assertTrue(payload["should_run"])
41-
self.assertEqual(payload["safe_task_count"], 2)
42-
self.assertEqual(payload["branch_name"], "automation/monthly-optimization-issue-15")
43-
self.assertEqual(payload["safe_actions"][0]["title"], "Add zero-trade diagnostics to the report")
45+
self.assertFalse(payload["should_run"])
46+
self.assertEqual(payload["safe_task_count"], 0)
47+
self.assertEqual(payload["skipped_task_count"], 2)
48+
self.assertEqual(
49+
[action["title"] for action in payload["skipped_actions"]],
50+
[
51+
"Restore monthly shadow/challenger build generation before review",
52+
"Document deterministic tie-break behavior for equal-score boundary cases",
53+
],
54+
)
4455

4556
def test_render_pr_body_contains_marker_and_issue_reference(self) -> None:
46-
payload = build_payload(self.issue_context)
57+
issue_context = {
58+
"number": 30,
59+
"title": "Monthly Optimization Tasks · Sandbox",
60+
"body": """# Monthly Optimization Tasks · Sandbox
61+
62+
## Actions
63+
- [ ] `low` Add a short README note [auto-pr-safe]
64+
- Summary: Document a small operator-facing behavior.
65+
- Source: [Sandbox #1](https://example.com/issues/1)
66+
""",
67+
}
68+
with tempfile.TemporaryDirectory() as temp_dir:
69+
payload = build_payload(issue_context, repo_root=Path(temp_dir))
4770
body = render_pr_body(payload)
4871

49-
self.assertIn("<!-- auto-optimization-pr:issue-15 -->", body)
50-
self.assertIn("Add zero-trade diagnostics to the report", body)
51-
self.assertIn("Refs #15", body)
72+
self.assertIn("<!-- auto-optimization-pr:issue-30 -->", body)
73+
self.assertIn("Add a short README note", body)
74+
self.assertIn("Refs #30", body)
5275

5376

5477
if __name__ == "__main__":

0 commit comments

Comments
 (0)