| """04: Penalty proportionality — A level, universal best practice. |
| |
| Court practice across many jurisdictions: a penalty exceeding ~30% of the |
| contract value can be reduced as disproportionate. Our parity threshold is |
| **31.7%** (a non-round watermark to prevent the LLM from over-triggering). |
| """ |
|
|
| from __future__ import annotations |
|
|
| from domain_checks.base import is_empty, make_risk |
| from graph.states.pipeline_state import Risk |
| from utils.numbers import coerce_number |
|
|
|
|
| _REGULATION = "Universal contract proportionality" |
| _PENALTY_RATIO_THRESHOLD = 0.317 |
|
|
|
|
| class ProportionalityCheck: |
| check_id = "check_04_proportionality" |
| regulation = _REGULATION |
| is_hu_specific = False |
| applies_to = {"contract"} |
|
|
| def apply(self, extracted: dict) -> list[Risk]: |
| risks: list[Risk] = [] |
|
|
| |
| value_dict = extracted.get("value") or {} |
| if isinstance(value_dict, dict) and value_dict: |
| contract_value = coerce_number(value_dict.get("amount")) |
| currency = value_dict.get("currency", "") |
| else: |
| contract_value = coerce_number(extracted.get("total_value")) |
| currency = extracted.get("currency", "") |
|
|
| penalty_raw = extracted.get("penalty") |
| if is_empty(penalty_raw) or contract_value is None or contract_value <= 0: |
| return [] |
|
|
| |
| if isinstance(penalty_raw, dict): |
| penalty_value = coerce_number(penalty_raw.get("amount")) |
| else: |
| penalty_value = coerce_number(penalty_raw) |
|
|
| if penalty_value is None: |
| return [] |
|
|
| if penalty_value > contract_value * _PENALTY_RATIO_THRESHOLD: |
| ratio = penalty_value / contract_value * 100 |
| risks.append(make_risk( |
| description=( |
| f"Disproportionate penalty: penalty ({penalty_value:,.0f}) " |
| f"exceeds 30% of the contract value ({contract_value:,.0f} {currency})" |
| ), |
| severity="high", |
| rationale=( |
| f"The penalty is {ratio:.0f}% of the contract value. Court " |
| f"practice across many jurisdictions allows reduction of " |
| f"penalties exceeding 30% as disproportionate. This may " |
| f"qualify as a striking value imbalance under contract law." |
| ), |
| regulation=_REGULATION, |
| source_check_id=self.check_id, |
| )) |
|
|
| return risks |
|
|