feat: add typed tier assurance guardrails
This commit is contained in:
parent
3d0f614f49
commit
c65a2f1ff9
13 changed files with 727 additions and 7 deletions
|
|
@ -4,6 +4,8 @@ from dataclasses import dataclass, field
|
|||
from decimal import Decimal
|
||||
from typing import Any, Literal
|
||||
|
||||
from .pricing_models import PricingModel
|
||||
|
||||
GovernanceDecision = Literal["proceed", "approval_required", "blocked"]
|
||||
RecommendationType = Literal["research", "simulation", "model_change", "execution"]
|
||||
RecommendationPriority = Literal["high", "medium", "low"]
|
||||
|
|
@ -46,6 +48,7 @@ class ApprovalRequirement:
|
|||
approver_role: str
|
||||
reason: str
|
||||
blocking: bool = True
|
||||
evidence_refs: tuple[str, ...] = ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -138,6 +141,95 @@ class SafeTuningContract:
|
|||
notes: tuple[str, ...] = ()
|
||||
|
||||
|
||||
def assurance_claim_approval_requirements(
|
||||
model: PricingModel,
|
||||
previous_model: PricingModel | None,
|
||||
*,
|
||||
approver_role: str = "operator",
|
||||
) -> tuple[ApprovalRequirement, ...]:
|
||||
"""Return one-time approval gates for assurance changes at tier definition.
|
||||
|
||||
Callers must pass the previously approved definition. Publication and
|
||||
campaign workflows must not call this for an unchanged model: Tenancy
|
||||
Posture section 11.3 records the approval once when a tier is defined.
|
||||
"""
|
||||
|
||||
current = {claim.id: claim for claim in model.assurance_claims}
|
||||
previous = {
|
||||
claim.id: claim
|
||||
for claim in (previous_model.assurance_claims if previous_model is not None else ())
|
||||
}
|
||||
changed_ids = sorted(
|
||||
claim_id
|
||||
for claim_id in current.keys() | previous.keys()
|
||||
if current.get(claim_id) != previous.get(claim_id)
|
||||
)
|
||||
|
||||
requirements: list[ApprovalRequirement] = []
|
||||
for claim_id in changed_ids:
|
||||
claim = current.get(claim_id)
|
||||
prior_claim = previous.get(claim_id)
|
||||
evidence_ref = (claim or prior_claim).evidence_ref
|
||||
change = "removed" if claim is None else "added" if prior_claim is None else "changed"
|
||||
requirements.append(
|
||||
ApprovalRequirement(
|
||||
id=f"assurance-claim-{claim_id}-approval",
|
||||
title=f"Assurance claim '{claim_id}' approval",
|
||||
approver_role=approver_role,
|
||||
reason=(
|
||||
f"Tier '{model.id}' {change} customer assurance claim '{claim_id}'. "
|
||||
f"Review its typed minimums and evidence before the tier definition is accepted."
|
||||
),
|
||||
evidence_refs=(evidence_ref,),
|
||||
)
|
||||
)
|
||||
return tuple(requirements)
|
||||
|
||||
|
||||
def assess_tier_definition_assurance(
|
||||
model: PricingModel,
|
||||
previous_model: PricingModel | None,
|
||||
*,
|
||||
approver_role: str = "operator",
|
||||
) -> GovernanceAssessment:
|
||||
approvals = assurance_claim_approval_requirements(
|
||||
model,
|
||||
previous_model,
|
||||
approver_role=approver_role,
|
||||
)
|
||||
if approvals:
|
||||
return GovernanceAssessment(
|
||||
decision="approval_required",
|
||||
summary="Approval required for a changed customer assurance claim.",
|
||||
approvals=approvals,
|
||||
risks=(
|
||||
GovernanceRisk(
|
||||
id="unapproved-assurance-change",
|
||||
severity="high",
|
||||
summary="The tier definition changes a customer-visible assurance obligation.",
|
||||
mitigation="Validate the typed minima, inspect the attached evidence, and record human approval once for this tier revision.",
|
||||
),
|
||||
),
|
||||
supporting_observations=tuple(
|
||||
SupportingObservation(
|
||||
id=f"{approval.id}-evidence",
|
||||
title="Assurance evidence",
|
||||
summary="Evidence attached to the tier-definition approval gate.",
|
||||
source_ref=approval.evidence_refs[0],
|
||||
)
|
||||
for approval in approvals
|
||||
),
|
||||
notes=("Do not reuse this definition-time gate as a per-campaign approval.",),
|
||||
)
|
||||
return GovernanceAssessment(
|
||||
decision="proceed",
|
||||
summary="No assurance claim changed since the approved tier definition.",
|
||||
approvals=(),
|
||||
risks=(),
|
||||
supporting_observations=(),
|
||||
)
|
||||
|
||||
|
||||
def governance_policy_from_dict(raw: dict[str, Any]) -> GovernancePolicy:
|
||||
return GovernancePolicy(
|
||||
policy_id=raw.get("policy_id", "default-governance-policy"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue