symfonic.capabilities.tools.execution.preconditions¶
preconditions ¶
Preconditions — may this call run at all? (T3.1.3)
Two facts the registry has recorded since v7 and nothing ever enforced:
ToolRegistration.requires (the capability a tool needs) and
max_calls_per_invocation (its per-invocation budget). T3.1.2 lifted
both onto :class:~symfonic.capabilities.tools.values.ToolDescriptor and
left them unused, naming this task as their home.
The composition rule is fail open: a check that raises admits the call. A predicate bug that silently turned into a deny-all would take every tool offline at once and look, from the transcript, exactly like a model that stopped calling tools.
CallBudget ¶
Enforce max_calls_per_invocation, per tool, per invocation.
Counting is stateful by necessity — a budget is a fact about the
invocation, not about the call — so the object is scoped to one
invocation and :meth:reset starts the next one.
Source code in src/symfonic/capabilities/tools/execution/preconditions.py
CapabilityRequirement ¶
CapabilityRequirement(descriptors: Mapping[str, ToolDescriptor], *, available: frozenset[type] = frozenset())
Block a tool whose declared requires capability is not present.
A tool the catalogue has never heard of is admitted: "the catalogue does not know this tool" is a wiring bug and must stay distinguishable from "a precondition forbids this tool". That is T3.1.2's rule for the policy stage, applied at the execution seam.
Source code in src/symfonic/capabilities/tools/execution/preconditions.py
Precondition ¶
Bases: Protocol
A named check that may object to one call.
PreconditionSet ¶
descriptors_by_name ¶
Read a :class:~symfonic.capabilities.tools.catalog.ToolCatalog
(or anything iterable of descriptors) into the mapping the checks want.