Skip to content

symfonic.capabilities.tools.execution.skill_gate

skill_gate

A learned skill may say when it applies, and this is what makes that bite.

A procedural skill can carry a precondition: state that must hold before the action it describes is the right one. The predicate language and its evaluator already ship -- symfonic.memory.layers.procedural.predicates -- and until now nothing on the kernel path consulted them, so a skill promoted with a precondition was a skill whose precondition was decoration.

This is a :class:~.preconditions.Precondition, which means it sits at the one seam that can actually stop a call: the executor evaluates the set before it dispatches, and the first objection wins. Routing is a different question and a weaker one -- it narrows which tools the model is offered, and a model that asks anyway is not bound by it.

Unreachable rules admit; broken rules do not. A skill store that cannot be reached means there may be no rule for this call at all, and refusing every tool because a database is down takes an agent offline for a reason unrelated to what it was asked to do -- so that case declares itself unavailable and the call proceeds, reported. Everything else closes: a predicate that is present but malformed, an evaluator that raises, a state the rule cannot read. Each of those is a gate that failed while a rule existed, and admitting on them would turn an enforcement fault into a permission.

Only skills that name this tool. A skill about deploying says nothing about whether a search may run, so it is not consulted for one. Matching is on the same field routing matches on, action_tool, because two answers to "which tool is this skill about" is how they start to disagree.

SkillPrecondition

SkillPrecondition(skills: Callable[[], Awaitable[Any]], state: Callable[[], Any])

Block a call whose governing skill says its state does not hold.

Parameters:

Name Type Description Default
skills Callable[[], Awaitable[Any]]

awaited for this turn's procedural hits -- the same callable shape the router takes, so a deployment wires one source of skills rather than two.

required
state Callable[[], Any]

the turn's state, read when a call is being judged rather than captured at construction. A precondition about what has happened must be evaluated against what has happened.

required
Source code in src/symfonic/capabilities/tools/execution/skill_gate.py
def __init__(
    self,
    skills: Callable[[], Awaitable[Any]],
    state: Callable[[], Any],
) -> None:
    """
    Args:
        skills: awaited for this turn's procedural hits -- the same
            callable shape the router takes, so a deployment wires one
            source of skills rather than two.
        state: the turn's state, read when a call is being judged rather
            than captured at construction. A precondition about what has
            happened must be evaluated against what has happened.
    """
    self._skills = skills
    self._state = state