Skip to content

fix(buzz-agent): keep goose's bundled skills out of Buzz agents - #6867

Closed
micspiral wants to merge 1 commit into
micn/buzz-agent-goose-corefrom
dario/gdk-skills-builtin-optout
Closed

fix(buzz-agent): keep goose's bundled skills out of Buzz agents#6867
micspiral wants to merge 1 commit into
micn/buzz-agent-goose-corefrom
dario/gdk-skills-builtin-optout

Conversation

@micspiral

Copy link
Copy Markdown
Collaborator

What

Stops Buzz agents advertising goose's two compiled-in skills, goose-doc-guide and web-search.

Stacked on #3262 (micn/buzz-agent-goose-core), not on main — the code it changes only exists on that branch.

Why

buzz-agent on main scanned four filesystem directories for skills and had no builtins at all (hints.rs:8, :204-217). The goose swap registers goose's skills extension by name, so the platform factory runs SkillsClient::new (platform_extensions/mod.rs:219-221) with builtins on. Neither builtin is a Buzz skill, and web-search (added upstream in goose 6d6abf74a, arrived here with the d1d8f45 pin bump) instructs the model to shell out to uvx ddgs / Tavily / SearXNG — a capability Buzz does not provide.

That is a scope widening as a side effect of a port that is supposed to be behaviour-preserving.

How

with_builtin_skills(false) is only reachable off the constructor, so buzz builds the client itself and registers it with ExtensionManager::add_client. The prompt index is filtered to match — a skill in the index but absent from the client is a dead load_skill reference.

Two things that route had to preserve:

  • Bare tool name. is_unprefixed_extension (extension_manager.rs:392-400) keys off the ExtensionConfig, not the registration route. This passes the same ExtensionConfig::Platform { name: "skills" } the factory does, so the table's unprefixed_tools: true still applies and the model still sees load_skill, not skills__load_skill.
  • Working directory. The context is given the session explicitly, because SkillsClient::new reads session.working_dir and falls back to the process cwd without it — which for a desktop-spawned agent is not the nest.

Measured, not reasoned

Real ~/.buzz nest, built binary, local fake SSE provider (no model call, no meter). Same probe against the a41b78f03 binary as baseline:

a41b78f03 this branch
Skill index 13 skills 11 skills
Tool list ["load_skill"] ["load_skill"]
load_skill("buzz-cli") body returned body returned
load_skill("web-search") body returned Skill 'web-search' not found.
System prompt 12,632 B 12,079 B

Tests

tests/skills.rs gains assertions it was missing. The existing test checked only stopReason == end_turn plus a second round — a load_skill returning "Skill not found." passes both, so it could not tell a working skills path from a broken one. It now asserts the skill body arrived as a tool result.

The new test pins the builtins from both sides (absent from the index AND unresolvable through the tool); index-only would pass while the tool still served them. Verified to fail on the unpatched lib.rs, so the gate is known to reject.

buzz-agent: 111 unit + 36 integration green, clippy --all-targets -D warnings clean, fmt --check clean, at 0a4c78e1e.

Not in scope

~/.config/goose/skills is still scanned — an operator's personal goose skills apply to their own agents. .claude/skills was already scanned on main; the only new claude path is the global one, which is absent on both lab boxes. Hint loading is effectively unchanged. All three are deliberate, per direction in #feature-buzz-with-gdk.

goose's skills platform factory calls plain `SkillsClient::new`
(`platform_extensions/mod.rs:219-221`), which leaves goose's two compiled-in
skills switched on: `goose-doc-guide` and `web-search`
(`goose/src/skills/builtins/`). Neither is a Buzz skill. `web-search` tells the
model to shell out to `uvx ddgs` / Tavily / SearXNG — a capability Buzz does not
provide and never advertised on main, where `buzz-agent` scanned four
filesystem directories and had no builtins at all (`hints.rs:8`, `:204-217`).
So the goose swap widened every Buzz agent's advertised surface as a side
effect, which is not what "same agent, goose underneath" means.

`with_builtin_skills(false)` is only reachable off the constructor, so buzz
builds the client itself and registers it with `ExtensionManager::add_client`
instead of by name. The prompt index is filtered to match: a skill listed in
the index but absent from the client is a dead `load_skill` reference.

The `add_client` route keeps the bare tool name. `is_unprefixed_extension`
(`extension_manager.rs:392-400`) keys off the `ExtensionConfig`, not the
registration route, and this passes the same
`ExtensionConfig::Platform { name: "skills" }` the factory does, so the table's
`unprefixed_tools: true` still applies. The context is given the session
explicitly because `SkillsClient::new` reads `session.working_dir` for
discovery and falls back to the process cwd without it.

Measured against the real `~/.buzz` nest with the built binary and a local
fake SSE provider (no model call): index 13 skills -> 11, tool list still
`["load_skill"]`, `load_skill("buzz-cli")` still returns its body,
`load_skill("web-search")` now returns "not found". System prompt 12,632 ->
12,079 bytes.

`tests/skills.rs` gains two assertions it was missing. The existing test only
checked `stopReason == end_turn` and that a second round happened — a
`load_skill` answering "Skill not found." passes both, so the suite could not
distinguish a working skills path from a broken one. It now asserts the skill
body reached the model as a tool result. The new test pins the builtins from
both sides (absent from the index AND unresolvable through the tool) and was
verified to fail on the unpatched `lib.rs`.

buzz-agent: 111 unit + 36 integration green, `clippy --all-targets -D warnings`
clean, `fmt --check` clean.

Co-authored-by: Michael Neale <michael.neale@gmail.com>
Signed-off-by: Michael Neale <michael.neale@gmail.com>
@micspiral
micspiral requested a review from a team as a code owner August 26, 2026 07:18
@micspiral

Copy link
Copy Markdown
Collaborator Author

Closing — this should not have been a PR against a feature branch. The fix stays available as the single commit 0a4c78e1e on dario/gdk-skills-builtin-optout for whoever owns #3262 to cherry-pick.

@micspiral micspiral closed this Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants