ctrlrun.needs_approval — function, defined at src/ctrlrun/adapter.py:430
True iff the combined
SPEC-v0.3 §4.6 decision is APPROVE.
It is core’s rather than each adapter’s because the only way to write it in an adapter was
to build an Action, and Action.principal has no default: the principal would have come
from the framework’s session, which is the one thing §4.2 forbids. Here it comes from
Control.resolve_principal, exactly as it does at every other entry point.
It writes nothing: no event, no receipt, no request, no reservation. A framework may
call its predicate more than once, and a predicate that left evidence behind would put a
proposal in the log for every time the framework wondered.
A DENY returns False, so the tool is invoked and Control.execute denies it with a
receipt, an ACTION_DENIED and the exception the caller catches. Refusing here would refuse
without evidence, and SPEC-v0.3 §4.3 is explicit that a denial with a principal to attribute
it to belongs in the evidence log.
resource is a template over arguments, as @protect’s is (v0.1 §5.1), and the policy’s
resource: is used where none is given — the same precedence @protect applies. It
matters: authority matches on resource patterns (SPEC-v0.3 §4.2), so a predicate that
skipped it would evaluate a different action from the one that runs.
task and hop are that same argument one frame further out (SPEC-v0.10 §9). Without them
this predicate evaluates against the receiver’s whole candidate set while execute
evaluates against the hop alone (§2.3), so it answers “no human needed” for a call
execute then refuses. That is not a wider grant — Control.execute is the enforcement
point and decides against the hop either way — but it costs the framework its own approval
item, and a human is not asked before an invocation that then fails.