Skip to content

feat(risk): add evidence-backed gate assessment - #285

Merged
Pigbibi merged 2 commits into
mainfrom
codex/qsl-p4-slice1-qpk-risk-evidence-api-20260804
Aug 4, 2026
Merged

feat(risk): add evidence-backed gate assessment#285
Pigbibi merged 2 commits into
mainfrom
codex/qsl-p4-slice1-qpk-risk-evidence-api-20260804

Conversation

@Pigbibi

@Pigbibi Pigbibi commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add immutable qsl.risk_gate_assessment.v1 receipts for MEMBER and ACCOUNT gates
  • fail closed on non-approve RiskEngine actions and clear rejection budgets
  • enforce the unmandated 10%/single-position fallback

Validation

  • PYTHONPATH=src python3 -m pytest -q (786 passed, 1 skipped)
  • PYTHONPATH=src python3 -m ruff check .
  • PYTHONPATH=src python3 -m compileall -q src
  • python3 -m build --no-isolation

Co-Authored-By: Codex <noreply@openai.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 18fb5b04a7

ℹ️ 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".

Comment thread src/quant_platform_kit/risk/gate.py Outdated
Comment thread src/quant_platform_kit/risk/gate.py
Comment thread src/quant_platform_kit/risk/gate.py
Comment thread src/quant_platform_kit/risk/gate.py
Comment thread src/quant_platform_kit/risk/gate.py Outdated
Comment thread src/quant_platform_kit/risk/gate.py
Comment thread src/quant_platform_kit/risk/gate.py
Co-Authored-By: Codex <noreply@openai.com>
@Pigbibi
Pigbibi merged commit b371322 into main Aug 4, 2026
8 checks passed
@Pigbibi
Pigbibi deleted the codex/qsl-p4-slice1-qpk-risk-evidence-api-20260804 branch August 4, 2026 07:21

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69e838348a

ℹ️ 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".

):
return {}, {"invalid_mandate"}
authority_scope = mandate_provenance["authority_scope"]
receipt_sha256 = _sha256(mandate_provenance["authority_receipt_sha256"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify the mandate receipt before applying its limits

When mandate provenance can be altered or supplied by an untrusted caller, assess_with_evidence() accepts any 64-character lowercase hex string as the authority receipt and never checks that it corresponds to the mandate fields being enforced. A caller can therefore retain a valid-looking receipt while raising effective_exposure_cap, expanding allowed_nonzero_assets, or changing product factors, and the gate will issue an approval using authority that the referenced receipt never granted; resolve the receipt from a trusted source or verify it against a canonical mandate payload before using those limits.

Useful? React with 👍 / 👎.

Comment on lines +411 to +412
decision_digest_sha256=_canonical_digest(decision_payload),
portfolio_snapshot_digest_sha256=_canonical_digest(snapshot_payload),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind every risk-engine input to the assessment

When configured plugins inspect portfolio positions, snapshot metadata, or market_data, the receipt cannot prove which inputs they assessed: the snapshot digest covers only as_of, exposure, and equity, while no market-data digest is recorded at all. An approval produced from safe holdings or market conditions can consequently be paired with different inputs that the same plugin would reject, yet all recorded input digests still match; hash the full canonical engine inputs or pass only the already-digested normalized payload to the engine.

Useful? React with 👍 / 👎.

Comment on lines +310 to +314
for budget in decision.budgets or ():
amount = _finite_number(getattr(budget, "amount", None))
if amount is None or amount < 0.0:
return {"invalid_decision_budget"}
requested_budget += amount

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize budget units before comparing loss authority

When a decision uses any BudgetIntent.unit other than the mandate's implicit loss-budget unit, _budget_authority_errors() ignores the unit and compares the raw amount directly. For example, a mandate with a 100-quote-currency loss budget approves BudgetIntent(amount=1, unit="BTC"), potentially authorizing far more than the mandate permits; normalize supported units to a common denomination or reject budgets whose unit cannot be matched to the mandate.

Useful? React with 👍 / 👎.

Comment on lines +377 to +378
if weight > min(product_cap, nominal_cap):
reason_codes.add("product_exposure_cap")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow floating-point tolerance at product caps

When a target is computed by ordinary floating-point arithmetic at exactly the authorized product cap, this strict comparison can reject it spuriously. For example, 0.1 + 0.2 produces 0.30000000000000004, so a mathematically 30% target is rejected against a 30% product cap even though the overall exposure checks below already use a 1e-9 tolerance; apply the same tolerance here.

Useful? React with 👍 / 👎.

Comment on lines +388 to +392
risk_action = build_risk_engine().assess(
decision,
portfolio_snapshot,
market_data=market_data,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve risk-reduced actions in the evidence gate

When a configured plugin produces ROUTE_RISK_REDUCED, the checked RiskEngine flow resolves it to a risk_reduced action with a 0.5 scalar, but RiskEngine.assess() then collapses every action except blocked and risk_off into approve. Calling that wrapper here therefore approves and returns the original full-size decision instead of rejecting or scaling it; preserve the resolved action so this gate can enforce its non-approve check.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant