Skip to main content
Attesta defines three core enumerations that appear throughout the API: RiskLevel for discrete risk classifications, Verdict for approval outcomes, and ChallengeType for verification challenge kinds.

RiskLevel

Discrete risk classification derived from a continuous 0-1 score.

Import

from attesta import RiskLevel

Values

MemberValueScore RangeDefault ChallengeColor
LOW"low"0.0 - 0.3auto_approveGreen
MEDIUM"medium"0.3 - 0.6confirmYellow
HIGH"high"0.6 - 0.8quizRed
CRITICAL"critical"0.8 - 1.0multi_partyBright Red

from_score() Classmethod

Maps a continuous risk score to a discrete level.
from attesta import RiskLevel

RiskLevel.from_score(0.15)  # RiskLevel.LOW
RiskLevel.from_score(0.45)  # RiskLevel.MEDIUM
RiskLevel.from_score(0.72)  # RiskLevel.HIGH
RiskLevel.from_score(0.91)  # RiskLevel.CRITICAL

# Boundary values
RiskLevel.from_score(0.0)   # RiskLevel.LOW
RiskLevel.from_score(0.3)   # RiskLevel.MEDIUM (>= 0.3)
RiskLevel.from_score(0.6)   # RiskLevel.HIGH   (>= 0.6)
RiskLevel.from_score(0.8)   # RiskLevel.CRITICAL (>= 0.8)
RiskLevel.from_score(1.0)   # RiskLevel.CRITICAL

# Out-of-range raises ValueError
RiskLevel.from_score(1.5)   # ValueError: Risk score must be in [0, 1], got 1.5
RiskLevel.from_score(-0.1)  # ValueError: Risk score must be in [0, 1], got -0.1

Signature

@classmethod
def from_score(cls, score: float) -> RiskLevel
ParameterTypeDescription
scorefloatA risk score in the range [0.0, 1.0].
Returns: The corresponding RiskLevel. Raises: ValueError if score is outside [0.0, 1.0].

Using in Configuration

Risk levels are used as keys in the challenge map configuration:
attesta.yaml
challenge_map:
  low: auto_approve
  medium: confirm
  high: quiz
  critical: multi_party
And as values for the risk= parameter on the @gate decorator:
Python
from attesta import gate

@gate(risk="high")     # String value
@gate(risk=RiskLevel.HIGH)  # Enum value -- both work
def dangerous_action():
    pass
CRITICAL-level actions are never downgraded by the trust engine, regardless of how much trust an agent has accumulated. This is an intentional safety invariant — CRITICAL actions always require full multi-party verification.

Verdict

The outcome of an Attesta review. Determines whether the protected function executes.

Import

from attesta import Verdict

Values

MemberValueFunction Executes?Description
APPROVED"approved"YesThe action was approved by the operator or auto-approved.
DENIED"denied"NoThe operator explicitly rejected the action or failed the challenge.
MODIFIED"modified"Yes (modified)The action was approved with modifications (e.g., reduced scope).
TIMED_OUT"timed_out"NoThe review period expired without a response.
ESCALATED"escalated"NoThe action was escalated to a higher authority and is not yet resolved.

Behavior in @gate

The @gate decorator maps verdicts to behavior:
Verdict@gate Behavior
APPROVEDFunction executes normally, return value is passed through.
DENIEDAttestaDenied is raised. Function never executes.
MODIFIEDFunction executes (the modification field on ApprovalResult describes changes).
TIMED_OUTAttestaDenied is raised with message “Action timed out: …”.
ESCALATEDAttestaDenied is raised with message “Action escalated (not yet resolved): …”.

Checking Verdicts

from attesta import Attesta, ActionContext, Verdict

attesta = Attesta.from_config("attesta.yaml")
ctx = ActionContext(function_name="deploy", args=("api",))

result = await attesta.evaluate(ctx)

match result.verdict:
    case Verdict.APPROVED:
        print("Proceeding with action")
    case Verdict.DENIED:
        print("Action was denied")
    case Verdict.MODIFIED:
        print(f"Action modified: {result.modification}")
    case Verdict.TIMED_OUT:
        print("Review timed out")
    case Verdict.ESCALATED:
        print("Action escalated -- awaiting resolution")
The MODIFIED verdict is currently only produced by custom renderers that return it from render_approval(). The built-in renderers never produce MODIFIED — they return either APPROVED or DENIED.

ChallengeType

The kind of verification challenge presented to the operator. Determines the level of cognitive engagement required before the action can proceed.

Import

from attesta import ChallengeType

Values

MemberValueRisk LevelDescription
AUTO_APPROVE"auto_approve"LOWNo human interaction. Action proceeds silently.
CONFIRM"confirm"MEDIUMSimple Y/N prompt. Operator must review the action details and confirm. A minimum review time may be enforced.
QUIZ"quiz"HIGH1-3 auto-generated comprehension questions about the action (file paths, parameters, affected resources).
TEACH_BACK"teach_back"HIGHOperator must explain in their own words what the action does (15+ words, key-term matching).
MULTI_PARTY"multi_party"CRITICAL2+ independent approvers, each completing a different sub-challenge (teach-back, quiz, confirm).

Default Challenge Map

The built-in mapping from risk level to challenge type:
Python
from attesta import RiskLevel, ChallengeType

DEFAULT_CHALLENGE_MAP = {
    RiskLevel.LOW:      ChallengeType.AUTO_APPROVE,
    RiskLevel.MEDIUM:   ChallengeType.CONFIRM,
    RiskLevel.HIGH:     ChallengeType.QUIZ,
    RiskLevel.CRITICAL: ChallengeType.MULTI_PARTY,
}

Custom Challenge Maps

You can override the default mapping per-gate or via configuration:
from attesta import gate, RiskLevel, ChallengeType

# Use teach-back for HIGH risk instead of quiz
@gate(challenge_map={
    RiskLevel.LOW: ChallengeType.AUTO_APPROVE,
    RiskLevel.MEDIUM: ChallengeType.CONFIRM,
    RiskLevel.HIGH: ChallengeType.TEACH_BACK,
    RiskLevel.CRITICAL: ChallengeType.MULTI_PARTY,
})
def sensitive_action():
    pass

Challenge Escalation in Multi-Party

For CRITICAL actions, the multi_party challenge type assigns different sub-challenges to each approver in descending order of rigor:
ApproverSub-Challenge
1stteach_back
2ndquiz
3rd+confirm
This ensures that at least one approver demonstrates deep comprehension of the action before it proceeds.
Downgrading CRITICAL actions to a simpler challenge type (e.g., confirm) via a custom challenge map is technically possible but strongly discouraged. CRITICAL actions represent the highest-risk operations and should always require multi-party verification in production environments.

String Conversion

All enums use lowercase string values. You can construct enum members from strings:
Python
from attesta import RiskLevel, Verdict, ChallengeType

# From string value
level = RiskLevel("high")           # RiskLevel.HIGH
verdict = Verdict("approved")       # Verdict.APPROVED
challenge = ChallengeType("quiz")   # ChallengeType.QUIZ

# To string value
level.value      # "high"
verdict.value    # "approved"
challenge.value  # "quiz"

# Invalid values raise ValueError
RiskLevel("unknown")  # ValueError: 'unknown' is not a valid RiskLevel