Skip to content

symfonic.capabilities.tools.stages.base

base

The one shape every tool-selection lever now has (T3.1.2).

SelectionStage

Bases: Protocol

One narrowing (or widening) lever in the selection pipeline.

name labels the stage in the decision trace.

protects_forced declares whether the pipeline reinstates a forced tool this stage dropped. It is True for stages that run after force resolution and express a preference (a role palette, a policy ceiling); a stage that must be able to drop a forced tool โ€” a hard security filter, say โ€” sets it False and says so.

apply never raises for a caller's benefit: the pipeline contains failures either way, but a stage that can degrade meaningfully should return StageOutcome.abstain(degraded=True) and log why.

filter_by_names

filter_by_names(tools: Sequence[Any], keep: set[str], *, keep_unnamed: bool = True) -> tuple[Any, ...]

Keep tools whose name is in keep, preserving input order.

keep_unnamed retains tools with no resolvable name. They cannot be gated by a name-keyed policy, and silently dropping them would turn a missing .name into an invisible behaviour change.

Source code in src/symfonic/capabilities/tools/stages/base.py
def filter_by_names(
    tools: Sequence[Any], keep: set[str], *, keep_unnamed: bool = True,
) -> tuple[Any, ...]:
    """Keep ``tools`` whose name is in ``keep``, preserving input order.

    ``keep_unnamed`` retains tools with no resolvable name. They cannot
    be gated by a name-keyed policy, and silently dropping them would
    turn a missing ``.name`` into an invisible behaviour change.
    """
    out: list[Any] = []
    for tool in tools:
        name = tool_name(tool)
        if name is None:
            if keep_unnamed:
                out.append(tool)
            continue
        if name in keep:
            out.append(tool)
    return tuple(out)