Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 3 additions & 100 deletions .github/workflows/monthly_optimization_planner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ name: Monthly Optimization Planner
upstream_run_id:
description: "AI review run id from CryptoSnapshotPipelines"
required: true
downstream_run_id:
description: "AI review run id from BinancePlatform"
required: true
downstream_repo:
description: "Downstream execution repo"
required: true
default: "QuantStrategyLab/BinancePlatform"

jobs:
planner:
Expand Down Expand Up @@ -50,7 +43,6 @@ jobs:
owner: ${{ github.repository_owner }}
repositories: |
CryptoSnapshotPipelines
BinancePlatform
CryptoStrategies
permission-actions: write
permission-issues: write
Expand Down Expand Up @@ -81,27 +73,16 @@ jobs:
--repo "${GITHUB_REPOSITORY}" \
--dir data/input/upstream

- name: Download downstream AI review artifact
env:
GH_TOKEN: ${{ steps.cross_repo_token.outputs.token }}
run: |
mkdir -p data/input/downstream
gh run download "${{ inputs.downstream_run_id }}" \
--repo "${{ inputs.downstream_repo }}" \
--dir data/input/downstream

- name: Resolve downloaded artifact paths
id: artifact_paths
run: |
UPSTREAM_DIR=$(find data/input/upstream -mindepth 1 -maxdepth 1 -type d | head -1)
DOWNSTREAM_DIR=$(find data/input/downstream -mindepth 1 -maxdepth 1 -type d | head -1)
if [ -z "${UPSTREAM_DIR}" ] || [ -z "${DOWNSTREAM_DIR}" ]; then
echo "Failed to resolve downloaded artifact directories" >&2
if [ -z "${UPSTREAM_DIR}" ]; then
echo "Failed to resolve downloaded upstream artifact directory" >&2
exit 1
fi
{
echo "upstream_dir=${UPSTREAM_DIR}"
echo "downstream_dir=${DOWNSTREAM_DIR}"
} >> "${GITHUB_OUTPUT}"

- name: Prepare upstream review payload
Expand All @@ -114,21 +95,10 @@ jobs:
--run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${{ inputs.upstream_run_id }}" \
--output-file data/output/prepared/upstream_review_payload.json

- name: Prepare downstream review payload
run: |
python3 scripts/build_ai_review_payload.py \
--source-repo "${{ inputs.downstream_repo }}" \
--review-kind execution_runtime \
--issue-context-file "${{ steps.artifact_paths.outputs.downstream_dir }}/issue_context.json" \
--secondary-review-file "${{ steps.artifact_paths.outputs.downstream_dir }}/secondary_review.json" \
--run-url "${GITHUB_SERVER_URL}/${{ inputs.downstream_repo }}/actions/runs/${{ inputs.downstream_run_id }}" \
--output-file data/output/prepared/downstream_review_payload.json

- name: Build monthly optimization plan
run: |
python3 scripts/build_monthly_optimization_plan.py \
--upstream-review-file data/output/prepared/upstream_review_payload.json \
--downstream-review-file data/output/prepared/downstream_review_payload.json \
--output-dir data/output/monthly_optimization

- name: Append optimization summary
Expand Down Expand Up @@ -174,18 +144,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ steps.cross_repo_token.outputs.token }}

- name: Fan out BinancePlatform task issue
run: |
python3 scripts/fanout_monthly_optimization_tasks.py \
--plan-file data/output/monthly_optimization/optimization_plan.json \
--owner-repo BinancePlatform \
--repo "${{ inputs.downstream_repo }}" \
--planner-issue-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/issues/${{ steps.optimization_issue.outputs.issue_number }}" \
--output-file data/output/monthly_optimization/fanout/binance_platform.json \
--allow-permission-skip
env:
GITHUB_TOKEN: ${{ steps.cross_repo_token.outputs.token }}

- name: Append fanout summary
run: |
python3 - <<'PY' >> "$GITHUB_STEP_SUMMARY"
Expand Down Expand Up @@ -235,63 +193,8 @@ jobs:
gh workflow run experiment_validation.yml \
-f issue_number="${{ steps.upstream_experiment_target.outputs.issue_number }}"

- name: Resolve downstream experiment validation target
id: downstream_experiment_target
run: |
python3 - <<'PY'
import json
import os
from pathlib import Path

fanout = json.loads(
Path("data/output/monthly_optimization/fanout/binance_platform.json").read_text(encoding="utf-8")
)
plan = json.loads(
Path("data/output/monthly_optimization/optimization_plan.json").read_text(encoding="utf-8")
)
actions = plan.get("repo_action_summary", {}).get("BinancePlatform", {}).get("actions", [])
should_dispatch = bool(fanout.get("issue_number")) and fanout.get("status") in {"created", "updated"} and any(
action.get("experiment_only") for action in actions
)
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
print(f"should_dispatch={'true' if should_dispatch else 'false'}", file=output)
print(f"issue_number={fanout.get('issue_number') or ''}", file=output)
PY

- name: Best-effort label BinancePlatform issue for experiment validation
if: steps.downstream_experiment_target.outputs.should_dispatch == 'true'
env:
GH_TOKEN: ${{ steps.cross_repo_token.outputs.token }}
TARGET_REPO: ${{ inputs.downstream_repo }}
ISSUE_NUMBER: ${{ steps.downstream_experiment_target.outputs.issue_number }}
run: |
set +e
gh label create experiment-validation --repo "$TARGET_REPO" --color 1D76DB --description "Trigger experiment validation for monthly optimization tasks" --force
label_status=$?
gh issue edit "$ISSUE_NUMBER" --repo "$TARGET_REPO" --add-label experiment-validation
issue_status=$?
set -e
if [ "$label_status" -ne 0 ] || [ "$issue_status" -ne 0 ]; then
echo "Downstream experiment-validation label update skipped for $TARGET_REPO#$ISSUE_NUMBER." >> "$GITHUB_STEP_SUMMARY"
fi

- name: Dispatch BinancePlatform experiment validation
if: steps.downstream_experiment_target.outputs.should_dispatch == 'true'
env:
GH_TOKEN: ${{ steps.cross_repo_token.outputs.token }}
run: |
set +e
gh workflow run experiment_validation.yml \
-R "${{ inputs.downstream_repo }}" \
-f issue_number="${{ steps.downstream_experiment_target.outputs.issue_number }}"
status=$?
set -e
if [ "$status" -ne 0 ]; then
echo "Downstream experiment validation dispatch skipped: ${{ steps.cross_repo_token.outputs.source }} token likely needs Actions write on ${{ inputs.downstream_repo }}." >> "$GITHUB_STEP_SUMMARY"
fi

- name: Upload planner artifact
uses: actions/upload-artifact@v7
with:
name: monthly-optimization-plan-${{ inputs.upstream_run_id }}-${{ inputs.downstream_run_id }}
name: monthly-optimization-plan-${{ inputs.upstream_run_id }}
path: data/output/monthly_optimization/
1 change: 0 additions & 1 deletion scripts/build_ai_review_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
SCHEMA_VERSION = "2026-04-02"
REPO_ROLE_BY_KIND = {
"upstream_selector": "upstream_selector_review",
"execution_runtime": "execution_runtime_review",
}


Expand Down
74 changes: 37 additions & 37 deletions scripts/build_monthly_optimization_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

RISK_ORDER = {"low": 0, "medium": 1, "high": 2}
SCHEMA_VERSION = "2026-04-02"
REPO_ORDER = ["CryptoSnapshotPipelines", "CryptoStrategies", "BinancePlatform"]
REPO_ORDER = ["CryptoSnapshotPipelines", "CryptoStrategies"]
MANUAL_REVIEW_PREFIXES = (
"check ",
"review ",
Expand All @@ -30,26 +30,6 @@ def _combined_action_text(action: dict[str, Any]) -> str:
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 (
Expand Down Expand Up @@ -127,15 +107,24 @@ def normalize_action(source_review: dict[str, Any], action: dict[str, Any]) -> d
}


def build_plan(upstream_review: dict[str, Any], downstream_review: dict[str, Any]) -> dict[str, Any]:
source_reviews = [upstream_review, downstream_review]
def build_plan(*source_reviews: dict[str, Any]) -> dict[str, Any]:
if not source_reviews:
raise ValueError("at least one source review is required")
source_review_list = list(source_reviews)

normalized_actions = [
normalize_action(review, action)
for review in source_reviews
for review in source_review_list
for action in review.get("recommended_actions", [])
]
in_scope_actions = [
action for action in normalized_actions if action["owner_repo"] in REPO_ORDER
]
out_of_scope_actions = [
action for action in normalized_actions if action["owner_repo"] not in REPO_ORDER
]
repo_groups: dict[str, list[dict[str, Any]]] = defaultdict(list)
for action in normalized_actions:
for action in in_scope_actions:
repo_groups[action["owner_repo"]].append(action)

repo_action_summary = {
Expand All @@ -148,30 +137,31 @@ def build_plan(upstream_review: dict[str, Any], downstream_review: dict[str, Any
if repo_groups.get(repo)
}

safe_auto_pr_candidates = [action for action in normalized_actions if action["auto_pr_safe"] and action["risk_level"] == "low"]
experiment_candidates = [action for action in normalized_actions if action["experiment_only"]]
safe_auto_pr_candidates = [action for action in in_scope_actions if action["auto_pr_safe"] and action["risk_level"] == "low"]
experiment_candidates = [action for action in in_scope_actions if action["experiment_only"]]
human_review_required = [
action for action in normalized_actions if (not action["auto_pr_safe"]) or action["risk_level"] != "low"
action for action in in_scope_actions if (not action["auto_pr_safe"]) or action["risk_level"] != "low"
]
operator_focus = [
f"{review['source_repo']}: {review['summary']}"
for review in source_reviews
for review in source_review_list
]

highest_review_risk = highest_risk([
{"risk_level": upstream_review["risk_level"]},
{"risk_level": downstream_review["risk_level"]},
{"risk_level": review["risk_level"]}
for review in source_review_list
])

return {
"schema_version": SCHEMA_VERSION,
"generated_at": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
"source_reviews": source_reviews,
"source_reviews": source_review_list,
"highest_review_risk": highest_review_risk,
"repo_action_summary": repo_action_summary,
"safe_auto_pr_candidates": sort_actions(safe_auto_pr_candidates),
"experiment_candidates": sort_actions(experiment_candidates),
"human_review_required": sort_actions(human_review_required),
"out_of_scope_actions": sort_actions(out_of_scope_actions),
"operator_focus": operator_focus,
}

Expand All @@ -184,6 +174,7 @@ def render_summary_markdown(plan: dict[str, Any]) -> str:
f"- Safe auto-PR candidates: `{len(plan['safe_auto_pr_candidates'])}`",
f"- Experiment candidates: `{len(plan['experiment_candidates'])}`",
f"- Human review required: `{len(plan['human_review_required'])}`",
f"- Out-of-scope downstream actions: `{len(plan.get('out_of_scope_actions', []))}`",
"",
"## Source Reviews",
]
Expand Down Expand Up @@ -218,24 +209,33 @@ def render_summary_markdown(plan: dict[str, Any]) -> str:
lines.extend(["", "## Operator Focus"])
lines.extend(f"- {item}" for item in plan["operator_focus"])

if plan.get("out_of_scope_actions"):
lines.extend(["", "## Out-of-Scope Actions"])
for action in plan["out_of_scope_actions"]:
lines.append(
f"- `{action['risk_level']}` {action['owner_repo']}: {action['title']} "
f"(from {action['source_repo']} #{action['source_issue_number']})"
)

return "\n".join(lines).strip() + "\n"


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Build the monthly optimization plan by combining upstream and downstream AI review payloads.",
description="Build the monthly optimization plan from upstream selector AI review payloads.",
)
parser.add_argument("--upstream-review-file", required=True, type=Path)
parser.add_argument("--downstream-review-file", required=True, type=Path)
parser.add_argument("--downstream-review-file", type=Path)
parser.add_argument("--output-dir", required=True, type=Path)
return parser.parse_args()


def main() -> int:
args = parse_args()
upstream_review = json.loads(args.upstream_review_file.read_text(encoding="utf-8"))
downstream_review = json.loads(args.downstream_review_file.read_text(encoding="utf-8"))
plan = build_plan(upstream_review, downstream_review)
source_reviews = [json.loads(args.upstream_review_file.read_text(encoding="utf-8"))]
if args.downstream_review_file:
source_reviews.append(json.loads(args.downstream_review_file.read_text(encoding="utf-8")))
plan = build_plan(*source_reviews)
args.output_dir.mkdir(parents=True, exist_ok=True)
(args.output_dir / "optimization_plan.json").write_text(
json.dumps(plan, ensure_ascii=False, indent=2) + "\n",
Expand Down
47 changes: 0 additions & 47 deletions scripts/prepare_auto_optimization_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,6 @@
"walk-forward",
"walk forward",
),
"BinancePlatform": (
"dca",
"rotation",
"eligibility gate",
"free usdt",
"cash flow",
"withdrawal",
"deposit",
"open position",
"execution",
"live trading",
"threshold",
"circuit breaker",
"capital threshold",
"allocation",
"sizing",
"liquidity",
"spread",
"adv",
),
"CryptoStrategies": (
"strategy",
"signal",
Expand All @@ -85,22 +65,6 @@
r"^src/",
r"^config/",
),
"BinancePlatform": (
r"^application/",
r"^infra/",
r"^strategy/",
r"^entrypoints/",
r"^main\.py$",
r"^runtime_support\.py$",
r"^live_services\.py$",
r"^degraded_mode_support\.py$",
r"^market_snapshot_support\.py$",
r"^trade_state_support\.py$",
r"^trend_pool_support\.py$",
r"^strategy_core\.py$",
r"^strategy_loader\.py$",
r"^strategy_registry\.py$",
),
"CryptoStrategies": (
r"^src/",
),
Expand Down Expand Up @@ -182,17 +146,6 @@ 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
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 (
"## Zero-Trade Diagnostics" in monthly_report
and "by_category_and_gate" in monthly_report
)

return False


Expand Down
Loading