Risk OS Red Try Stoa

Autonomy inference

Every agent candidate gets an autonomy_level — a static classification of how unattended its side-effecting reach appears to be, derived entirely from signals the scanner already computes. It's always present, regardless of whether declarations exist, the same way capabilities and highest_severity always are.

The ladder

LevelMeaning
recommend_onlyNo side-effecting path from model output was observed; output terminates in returns/logs/UI.
human_approvedA side-effecting path exists, and an approval construct was observed gating it.
bounded_autonomousA side-effecting path exists, no approval gate, but a hardcoded cap check or rate limiter bounds it.
unrestricted_autonomousA side-effecting path exists with no approval and no bounding.
indeterminateThe signals don't cleanly resolve. Never a guess — see below.

How it's derived

No second taint pass — autonomy inference composes signals the scanner already produces:

# recommend_only — no side-effecting sink
resp = llm.invoke(prompt)
return resp.choices[0].message.content

# human_approved — approval construct observed, AI003 did not fire
if not human_input("approve refund?"):
    return
stripe.Refund.create(payment_intent=order_id, amount=amount)

# bounded_autonomous — cap check, no approval
if amount > MAX_REFUND:
    raise ValueError("too much")
stripe.Refund.create(payment_intent=order_id, amount=amount)

# unrestricted_autonomous — neither
stripe.Refund.create(payment_intent=order_id, amount=amount)

indeterminate is a feature, not a bug

When a side-effecting sink is observed but nothing else correlates — no high-impact capability, no tool binding, no approval, no bounding — the classifier reports indeterminate with a stated reason, rather than defaulting to a level it can't actually justify. A false autonomy classification is worse than an admitted gap: it's exactly the kind of overconfident, unverifiable claim a static scanner should never make. Check autonomy_level.reason for why.

Where it shows up