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
33 changes: 8 additions & 25 deletions .github/workflows/runtime-target-lifecycle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ name: Runtime Target Lifecycle
on:
workflow_dispatch:
schedule:
# QMT has no configured broker runtime. One weekday no-order validation is
# sufficient for this daily strategy family and avoids pretending it has a
# continuously running execution service.
# Publish the intentional retirement state without invoking a strategy.
- cron: "17 22 * * 1-5"

concurrency:
Expand All @@ -22,9 +20,6 @@ jobs:
timeout-minutes: 15
env:
EXECUTION_EVIDENCE_SYNC_URL: ${{ vars.EXECUTION_EVIDENCE_SYNC_URL }}
QMT_DRY_RUN_ONLY: "true"
QMT_MARKET_HISTORY_PATH: data/fixtures/market_history.sample.csv
STRATEGY_PROFILE: cn_industry_etf_rotation
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
Expand Down Expand Up @@ -69,27 +64,15 @@ jobs:
python -m pip install -e .
python -m pip install --no-deps -e external/QuantPlatformKit -e external/CnEquityStrategies

- name: Validate disabled dry-run target
- name: Verify the QMT strategy rollout remains empty
id: validation
run: |
set -euo pipefail
python scripts/preflight_qmt_runtime.py
# Exit 2 from this smoke means the cycle itself completed but the
# deterministic risk gate kept target weights empty. That is an
# expected no-order outcome for a disabled target, not a runtime
# failure. Exit 1 still means the dry-run did not complete.
set +e
python scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py
smoke_status="$?"
set -e
if [ "$smoke_status" -eq 0 ]; then
exit 0
fi
if [ "$smoke_status" -eq 2 ]; then
echo "Dry-run completed with target weights safely blocked by the risk gate."
exit 0
fi
exit "$smoke_status"
python - <<'PYCODE'
from strategy_registry import get_supported_profiles_for_platform
if get_supported_profiles_for_platform("qmt"):
raise SystemExit("QMT retirement policy changed; review the lifecycle publisher")
print("QMT has no enabled strategy profiles")
PYCODE

- name: Map validation to shared lifecycle checks
if: ${{ always() }}
Expand Down
10 changes: 4 additions & 6 deletions tests/test_runtime_target_lifecycle_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
def test_disabled_qmt_lifecycle_workflow_stays_no_order() -> None:
workflow = (ROOT / ".github" / "workflows" / "runtime-target-lifecycle.yml").read_text(encoding="utf-8")

assert "python scripts/preflight_qmt_runtime.py" in workflow
assert "python scripts/smoke_cn_industry_etf_rotation_dry_run_e2e.py" in workflow
assert 'if [ "$smoke_status" -eq 2 ]; then' in workflow
assert "safely blocked by the risk gate" in workflow
assert 'if get_supported_profiles_for_platform("qmt"):' in workflow
assert "scripts/preflight_qmt_runtime.py" not in workflow
assert "dry_run_e2e.py" not in workflow
assert "configured-state: disabled" in workflow
assert "execution-mode: dry_run" in workflow
assert "execution_heartbeat=not_applicable" in workflow
assert "platform: qmt" in workflow
assert 'QMT_DRY_RUN_ONLY: "true"' in workflow
assert "QMT_DRY_RUN_ONLY=false" not in workflow
assert "STRATEGY_PROFILE:" not in workflow
Loading