feat: bind promotion runners to risk provenance - #294
Conversation
Co-Authored-By: Codex <noreply@openai.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7538d2b5d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| observed = float(observed_effective_exposure) | ||
| if ( | ||
| not math.isfinite(cap) | ||
| or not 0.0 <= cap <= _BOOTSTRAP_EFFECTIVE_EXPOSURE_CAP |
There was a problem hiding this comment.
Allow reduce-only validation to use mandate caps
The risk gate passes the mandate's effective_exposure_cap into this helper, and _mandate_fields accepts mandate caps up to 1.0. With this hard 0.50 bootstrap ceiling, a valid mandate such as cap=0.75 reducing an observed 1.0 exposure to a target 0.75 is marked invalid_reduce_only_normalization, so assess_with_evidence falls back to max(observed, target) and rejects with exposure errors instead of allowing the reduce-only transition. Use the mandate cap directly rather than the bootstrap constant here.
Useful? React with 👍 / 👎.
| "candidate_identity_sha256": self.candidate_identity_sha256, | ||
| "decision_digest_sha256": self.decision_digest_sha256, | ||
| "portfolio_snapshot_digest_sha256": self.portfolio_snapshot_digest_sha256, | ||
| "normalization_origin_digest_sha256": self.normalization_origin_digest_sha256, |
There was a problem hiding this comment.
Bump the risk assessment contract version
Adding candidate_identity_sha256 and normalization_origin_digest_sha256 to the canonical RiskGateAssessment payload changes both the required receipt shape and the assessment_sha256 computation, but assess_with_evidence still emits qsl.risk_gate_assessment.v1. Consumers comparing or validating stored v1 receipts cannot distinguish old hashes from the new schema, so this should use a new contract version when these fields participate in the digest.
Useful? React with 👍 / 👎.
| scope: str, | ||
| mandate_provenance: Mapping[str, Any] | None, | ||
| market_data: Mapping[str, Any], | ||
| candidate_identity: CandidateRiskIdentity | None = None, |
There was a problem hiding this comment.
Export the typed candidate identity from the risk facade
Mandated calls to assess_with_evidence now reject without a CandidateRiskIdentity, but the package facade in src/quant_platform_kit/risk/__init__.py still does not import or list that class in __all__. Callers using the existing quant_platform_kit.risk API can access assess_with_evidence but cannot construct the required typed identity from the same public surface, causing integrations to hit missing_candidate_identity unless they know to reach into risk.contracts.
Useful? React with 👍 / 👎.
| runner_kind = getattr(runner, "runner_kind", None) | ||
| if runner_kind != "real": |
There was a problem hiding this comment.
Keep ordinary optimizer runners compatible
In the run_optimization path, _auto_register_runner is registering an ordinary BacktestRunner that is then used by run_grid_search, and that protocol only requires run(...); it does not include a runner_kind marker. With this exact check, existing real domain factories that predate the marker now raise RuntimeError before any non-promotion optimization can run, even though promotion execution is separately gated in BacktestOrchestrator.run_promotion.
Useful? React with 👍 / 👎.
Summary
runner_kind="real"marker for promotion-runner registration and execution while preserving ordinary non-promotion test runnersRiskEngine.assessVerification
89 passed, 73 subtests passed1 passed893 passed, 1 skipped, 76 subtests passed--no-isolation,/tmp): PASSBoundaries