Skip to content

feat(skills): exclude_keywords veto in skill activation scoring#688

Open
nick-stebbings wants to merge 2 commits intonearai:mainfrom
nick-stebbings:pr/skills-exclude-keywords-v2
Open

feat(skills): exclude_keywords veto in skill activation scoring#688
nick-stebbings wants to merge 2 commits intonearai:mainfrom
nick-stebbings:pr/skills-exclude-keywords-v2

Conversation

@nick-stebbings
Copy link

Summary

Adds exclude_keywords support to the skill activation scoring pipeline. If any exclude keyword is present in the user message, the skill scores 0 regardless of keyword or pattern matches.

This prevents cross-skill interference — e.g. a writing skill with keywords: ["write", "draft"] and exclude_keywords: ["route", "redirect"] will not activate on messages like "don't route this to the writing agent".

Behaviour note: exclude_keywords is a hard veto — if any exclude keyword matches, the skill scores 0 regardless of keyword or pattern matches. This is intentional; partial exclusion (score reduction rather than veto) would create unpredictable interference behaviour.

Changes

  • mod.rs: New exclude_keywords field on ActivationCriteria, new lowercased_exclude_keywords on LoadedSkill
  • selector.rs: Early-return 0 in score_skill() if any exclude keyword matches
  • registry.rs: Populate lowercased_exclude_keywords from manifest at load time (follows existing pattern of preprocessing at load time, not score time)
  • Test helpers updated across mod.rs, selector.rs, attenuation.rs

SKILL.md format addition

activation:
  keywords: ["write", "draft"]
  exclude_keywords: ["route", "redirect"]

Test plan

  • Verify skill with exclude_keywords: ["route"] scores 0 when message contains "route"
  • Verify same skill scores normally when message does not contain any exclude keyword
  • Verify exclude_keywords is case-insensitive (preprocessed via to_lowercase() at load time)
  • cargo check and cargo test pass

🤖 Generated with Claude Code

Add exclude_keywords field to ActivationCriteria. If any exclude
keyword is present in the user message, the skill scores 0 regardless
of keyword or pattern matches — prevents cross-skill interference.

Behaviour: exclude_keywords is a hard veto. Even an exact skill name
match gets vetoed if an exclude keyword is also present. This is
intentional; partial exclusion (score reduction) would create
unpredictable interference behaviour.

Example use case: a writing skill with keywords ["write", "draft"]
and exclude_keywords ["route", "redirect"] will not activate on
messages like "don't route this to the writing agent".

Changes:
- ActivationCriteria: new exclude_keywords field (serde default)
- LoadedSkill: new lowercased_exclude_keywords (preprocessed at load)
- selector.rs: early-return 0 in score_skill() on veto match
- registry.rs: populate lowercased_exclude_keywords during loading
- Test helpers updated across mod.rs, selector.rs, attenuation.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the size: S 10-49 changed lines label Mar 7, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the skill activation system by introducing a robust exclusion mechanism. It allows skill definitions to specify keywords that, if present in a user's message, will completely prevent that skill from activating, regardless of other matching criteria. This feature is designed to eliminate undesirable cross-skill interference and improve the predictability of skill selection.

Highlights

  • Skill Activation Veto: Introduced exclude_keywords to ActivationCriteria to prevent skills from activating if specific keywords are present in the user message, acting as a hard veto.
  • Performance Optimization: Added lowercased_exclude_keywords to LoadedSkill and pre-processed them at load time to optimize scoring performance by avoiding per-message allocation and case conversion during runtime.
  • Scoring Logic Update: Modified the score_skill function to implement an early return with a score of 0 if any exclude_keyword is found in the message, ensuring immediate deactivation.
  • Skill Loading: Updated the skill loading process in registry.rs to populate the new lowercased_exclude_keywords field from the skill manifest.
  • Test Helper Updates: Adjusted test helper functions across mod.rs, selector.rs, and attenuation.rs to accommodate the new exclude_keywords field.
Changelog
  • src/skills/attenuation.rs
    • Added lowercased_exclude_keywords to the LoadedSkill struct within the test helper.
  • src/skills/mod.rs
    • Added a new exclude_keywords field to the ActivationCriteria struct.
    • Added a new lowercased_exclude_keywords field to the LoadedSkill struct.
    • Updated test helper test_skill() to include lowercased_exclude_keywords.
  • src/skills/registry.rs
    • Implemented logic to convert exclude_keywords to lowercase and store them in lowercased_exclude_keywords during skill loading.
    • Passed lowercased_exclude_keywords to the LoadedSkill constructor.
  • src/skills/selector.rs
    • Introduced an early return in score_skill that sets the score to 0 if any lowercased_exclude_keywords are found in the message.
    • Updated test helper create_test_skill() to include an empty exclude_keywords vector.
    • Updated test helper create_loaded_skill() to include an empty lowercased_exclude_keywords vector.
Activity
  • The pull request was generated with Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added risk: low Changes to docs, tests, or low-risk modules contributor: new First-time contributor labels Mar 7, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an exclude_keywords feature for skills, allowing a skill's activation score to be set to zero if a user's message contains any of these keywords. While the implementation correctly adds the necessary fields and applies the veto logic, the new exclude_keywords field lacks resource limits (truncation and minimum length) applied to other activation criteria like keywords and tags. This omission could be exploited to cause performance degradation (Denial of Service) during message processing, and the current keyword matching logic can lead to false positives, especially with empty strings. I recommend updating the enforce_limits method in src/skills/mod.rs to include exclude_keywords and considering more robust keyword detection. Additionally, there are a couple of suggestions to improve code clarity and reduce duplication by consolidating repeated logic into helper functions.

…er, use any()

- Add exclude_keywords to enforce_limits() with same min-length and cap
  rules as keywords — prevents empty string always-match and unbounded lists
- Extract to_lowercase_vec() helper to deduplicate three identical blocks
- Use idiomatic any() iterator instead of for loop in score_skill veto check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nick-stebbings
Copy link
Author

Updated: Added exclude_keywords to enforce_limits() with same min-length and count caps as keywords — fixes empty-string-matches-everything bug. Extracted to_lowercase_vec() helper to deduplicate three identical blocks in registry.rs. Replaced for loop with any() in score_skill veto check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: new First-time contributor risk: low Changes to docs, tests, or low-risk modules size: S 10-49 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant