-
Notifications
You must be signed in to change notification settings - Fork 7
feat: skill activation routing — prefer a matching skill over model defaults (closes #271) #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3447,6 +3447,21 @@ func (r *Runner) buildSkillCatalog() string { | |
|
|
||
| var b strings.Builder | ||
| b.WriteString("## Available Skills\n\n") | ||
| // Routing directive (issue #271): the catalog alone is inert — the model | ||
| // defaults to its own behavior unless explicitly told to consult the | ||
| // catalog FIRST. Without this, an installed skill whose description | ||
| // matches the request is ignored (the model answers from its defaults and | ||
| // the user has to say "use the skill"). The final sentence guards against | ||
| // over-routing: fall back to defaults only when nothing matches. | ||
| // | ||
| // The "authoritative over your general behavior" phrasing intentionally | ||
| // escalates skill-content authority. This is bounded and consistent with | ||
| // the trust model: skills are operator-installed (not user-supplied), the | ||
| // pre-existing preamble already said "follow them", and guardrails still | ||
| // gate all five gates regardless of what a skill body says. Prompt- | ||
| // injection-via-skill-content audits should treat this escalation as | ||
| // deliberate, not an oversight. | ||
| b.WriteString("Before answering any request from your own knowledge or default behavior, FIRST check whether it matches one of the skill descriptions below. If a skill matches, call `read_skill` to load it and follow its instructions instead of answering directly — skills exist precisely to override your defaults for these cases, and a loaded skill's instructions are authoritative over your general behavior. Only answer from your own defaults when NO skill matches the request.\n\n") | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit (no change needed, noting for future reviewers): "a loaded skill's instructions are authoritative over your general behavior" deliberately escalates skill-content authority in the system prompt. It's consistent with the trust model — skills are operator-installed, guardrails still gate all five gates, and the pre-existing preamble already said "follow them" — but prompt-injection-via-skill-content audits will trip on this line later, so it's worth being on record that the escalation is intentional and bounded. |
||
| b.WriteString("To use a skill, call `read_skill` with the skill name (the identifier before the colon) to load its full instructions, then follow them. " + | ||
| "`provides:` lists the capabilities inside a skill — they are documentation loaded with the skill, not tools you call directly.\n\n") | ||
| for _, entry := range catalogEntries { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor (design observation worth one sentence here):
descriptionnow serves two audiences. It's (a) the internal routing trigger in this catalog AND (b) the public capability summary projected into the A2A Agent Cardskills[](FWS-1). Trigger-rich descriptions —When the user asks the time ("what time is it", "current time", "clock"), …— will appear verbatim in the public card, reading like routing rules rather than capability summaries to external A2A callers. Defensible trade-off (trigger phrasing is arguably more informative for callers too), but currently an undocumented consequence: one sentence acknowledging the card projection makes it a deliberate choice, and a separatetriggers:field remains the escape hatch if the two audiences ever diverge.