from __future__ import annotations from typing import Mapping from tenant_engine.domain import Tenant, TenantLifecycle from tenant_engine.guardrail.model import EffectiveLimit, LimitValue, Provenance from tenant_engine.guardrail.registry import DEFAULT_REGISTRY, LimitRegistry # Per-key precedence, highest first: override -> plan -> grouping (or the # reserved profile) -> fail-closed floor. Evaluated *per key*, never per set, # so a plan supplying `spend.monthly` does not wipe out a grouping-derived # `entity.*`. def resolve_limit( key: str, *, tenant: Tenant, plan_limits: Mapping[str, LimitValue] | None = None, overrides: Mapping[str, LimitValue] | None = None, registry: LimitRegistry = DEFAULT_REGISTRY, ) -> EffectiveLimit: """Resolve one limit key for one tenant. Total and side-effect-free. Raises `UnknownLimitKeyError` for an unregistered key -- neither zero nor unlimited, both of which would be lies. """ definition = registry.get(key) override = (overrides or {}).get(key) if override is not None: return _clamp(EffectiveLimit(key, override, Provenance.OVERRIDE), tenant, definition.floor) plan_limit = (plan_limits or {}).get(key) if plan_limit is not None: return _clamp(EffectiveLimit(key, plan_limit, Provenance.PLAN), tenant, definition.floor) if tenant.is_reserved: # Reserved identifiers are ungrouped, so layer 3 cannot apply. They do # not fall through to the floor either: that would clamp the platform's # own identity to zero and take the platform down with it. return _clamp( EffectiveLimit(key, definition.reserved_default, Provenance.RESERVED), tenant, definition.floor, ) grouping_default = definition.grouping_defaults.get(tenant.grouping or "") if grouping_default is not None: return _clamp( EffectiveLimit(key, grouping_default, Provenance.GROUPING), tenant, definition.floor ) # Only reachable if the registry is internally inconsistent -- a grouping # added to GROUPINGS with no default declared. The registry validates this # at construction, so this is the second line of defence, not the first. # Provenance stays `fail_closed` so the condition is visible in a read # rather than mistaken for deliberate policy. return _clamp( EffectiveLimit(key, definition.floor, Provenance.FAIL_CLOSED), tenant, definition.floor ) def resolve_limits( *, tenant: Tenant, plan_limits: Mapping[str, LimitValue] | None = None, overrides: Mapping[str, LimitValue] | None = None, registry: LimitRegistry = DEFAULT_REGISTRY, ) -> dict[str, EffectiveLimit]: """Resolve every registered key. Every tenant resolves to exactly one effective value per key -- there is no "unset means unlimited". """ return { key: resolve_limit( key, tenant=tenant, plan_limits=plan_limits, overrides=overrides, registry=registry, ) for key in registry.keys } def _clamp(effective: EffectiveLimit, tenant: Tenant, floor: LimitValue) -> EffectiveLimit: """Apply the lifecycle clamp. Applied after precedence, and may only reduce. Follows the TEN-WP-0005 precedent: operations that only reduce privilege stay available while retired, loosening ones do not. A retired tenant's guardrails stay readable -- it is the values that clamp, not the endpoint. """ if tenant.lifecycle is TenantLifecycle.ACTIVE: return effective clamped = effective.value.reduced_to_floor(floor) if clamped == effective.value: return effective return EffectiveLimit(effective.key, clamped, Provenance.LIFECYCLE)