From 90dd93f6aa791942124f1f5f552357b78801fb1f Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:12:46 +0800 Subject: [PATCH] fix: clarify unconfirmed health actions Co-Authored-By: Codex --- ops/quant-monitor/README.md | 4 ++++ .../scripts/build_dashboard_snapshot.py | 14 +++++++------- .../tests/test_dashboard_snapshot.py | 19 ++++++++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/ops/quant-monitor/README.md b/ops/quant-monitor/README.md index 1b3b2f77..1f952ea8 100644 --- a/ops/quant-monitor/README.md +++ b/ops/quant-monitor/README.md @@ -39,6 +39,10 @@ Token 从 GCP Secret `quant-sentinel-telegram-bot-token` 加载;**不要**把 监控证据只触发研究审查,不自动修改策略代码、live 参数、仓位、风险预算,不自动 merge 或 deploy。成功记录策略劣化后,monitor 正常结束,不再重复通知人工。 +因此健康卡片里的 `canary_eligible` 和 +`pause_request_pending_confirmation` 是**证据状态**,不是券商侧已执行的事实。 +只有日后接入并回传执行回执的预授权运行时,才能把暂停或 canary 推进标为已完成。 + | 变量 | 说明 | |------|------| | `QUANT_SENTINEL_TELEGRAM_SECRET_NAME` | 默认 `quant-sentinel-telegram-bot-token` | diff --git a/ops/quant-monitor/scripts/build_dashboard_snapshot.py b/ops/quant-monitor/scripts/build_dashboard_snapshot.py index 01b1388b..1bea98be 100755 --- a/ops/quant-monitor/scripts/build_dashboard_snapshot.py +++ b/ops/quant-monitor/scripts/build_dashboard_snapshot.py @@ -24,9 +24,9 @@ MAX_STRATEGIES = 100 DECISIONS = { "healthy": { - "code": "auto_advance", - "label": "系统可自动推进下一阶段", - "reason": "机器检查通过;仅在预批准的 canary 预算内推进,不自动进入正常 live。", + "code": "canary_eligible", + "label": "满足 canary 条件,等待预授权执行器确认", + "reason": "机器检查通过;监控只生成证据。仅已接入的预授权 canary 执行器才能推进,绝不自动进入正常 live。", }, "watch": { "code": "stay_shadow", @@ -39,9 +39,9 @@ "reason": "健康度进入复核区,先确认数据、成本和风险。", }, "critical": { - "code": "auto_pause", - "label": "自动暂停 / 回滚复核", - "reason": "触发严重告警,系统先控制风险,不等待人工确认。", + "code": "pause_request_pending_confirmation", + "label": "已提出暂停请求,待实际运行时确认", + "reason": "触发严重告警;监控会隔离晋级并生成可审计的暂停请求。缺少运行时回执时,券商执行状态为未知。", }, } @@ -181,7 +181,7 @@ def _decision(status: str, requested_stage: str | None = None) -> dict[str, str] return { "code": "human_live_gate", "label": "机器检查通过,等待你批准正常 live", - "reason": "进入正常资金暴露前仍需人工确认,不会因健康分自动上线。", + "reason": "进入正常资金暴露前仍需人工确认;健康监控本身不具备下单、暂停或上线权限。", } return dict(DECISIONS.get(status, { "code": "evidence_missing", diff --git a/ops/quant-monitor/tests/test_dashboard_snapshot.py b/ops/quant-monitor/tests/test_dashboard_snapshot.py index c12fe85a..a96c1ed8 100644 --- a/ops/quant-monitor/tests/test_dashboard_snapshot.py +++ b/ops/quant-monitor/tests/test_dashboard_snapshot.py @@ -62,7 +62,7 @@ def test_normalizes_health_scores_and_review_evidence(self): self.assertEqual(payload["data_status"], "ready") self.assertEqual(payload["summary"]["healthy"], 1) self.assertEqual(strategy["review"]["requested_stage"], "shadow_candidate") - self.assertEqual(strategy["decision"]["code"], "auto_advance") + self.assertEqual(strategy["decision"]["code"], "canary_eligible") self.assertEqual(strategy["freshness"]["status"], "unknown") self.assertEqual(strategy["source_revision"], "https://example.invalid/revisions/rev-1") self.assertEqual(strategy["review"]["evidence_package_id"], "https://example.invalid/evidence-1") @@ -272,6 +272,23 @@ def test_healthy_live_candidate_still_waits_for_human(self): self.assertEqual(payload["strategies"][0]["decision"]["code"], "human_live_gate") + def test_critical_status_never_claims_an_unconfirmed_broker_pause(self): + with tempfile.TemporaryDirectory() as tmp: + health = Path(tmp) / "health.json" + health.write_text(json.dumps({"strategies": [{ + "strategy_profile": "critical_strategy", + "domain": "us_equity", + "status": "critical", + "overall_score": 12, + }]}), encoding="utf-8") + + payload = build_payload(health_file=health) + + decision = payload["strategies"][0]["decision"] + self.assertEqual(decision["code"], "pause_request_pending_confirmation") + self.assertIn("待实际运行时确认", decision["label"]) + self.assertNotIn("已停单", decision["reason"]) + if __name__ == "__main__": unittest.main()