symfonic.capabilities.extensions.admission¶
admission ¶
Which contributions survive composition, and in what order (T4.2.2).
Three rules, split from the composer so each one is readable on its own and so "why was my tool dropped?" is answered by a function with the reason in its body rather than by a branch inside a loop.
Reserved names win. A contributed tool whose name the host already owns is refused, not admitted-and-shadowed. That is AS-INT-3: an extension executes with the invoking request's authority, and taking over a first-party tool's name is the cheapest way to acquire more.
First declaration wins. Two extensions offering the same tool name, or the same fragment id, resolve by the composition's declared order — and the loser gets a diagnostic rather than silence. The pre-refactor MCP provider resolved the same collision by overwriting its routing map, with no record that a tool had been replaced and no way for an operator to find out.
Order is total. :func:fragment_key breaks every tie, so a compiled prompt
is byte-stable across runs and across whatever iteration order produced the
contributions.
admit_fragment ¶
admit_fragment(fragment: PromptFragment, admitted: list[PromptFragment], ids: set[str], diagnostics: list[ExtensionDiagnostic]) -> bool
Admit fragment unless its id is already taken.
Source code in src/symfonic/capabilities/extensions/admission.py
admit_tool ¶
admit_tool(tool: ToolContribution, admitted: list[ToolContribution], names: set[str], reserved: frozenset[str], diagnostics: list[ExtensionDiagnostic]) -> bool
Admit tool unless its name is reserved or already taken.
Returns True when the tool was admitted. The caller does not branch on
the result today; it is returned so a future caller counting admissions does
not have to re-derive it by diffing the list.
Source code in src/symfonic/capabilities/extensions/admission.py
fragment_key ¶
Total order over fragments: layer, declared order, extension, id.