Skip to content

User-local skill directories cannot shadow bundle-shipped skills; documented discovery order silently not honored when config.skills is set #335

Description

@danshapiro

Note: filed here because the affected component's repo (microsoft/amplifier-bundle-skills, tool-skills module) has issues disabled. The evidence cites that repo's source.

Problem

Documentation (the skills-instructions context injected into every session, and the authoring guide) states that local skill dirs (.amplifier/skills/ workspace, ~/.amplifier/skills/ user, AMPLIFIER_SKILLS_DIR) are discovered by default and take priority over bundle-shipped skills.

In practice, when any bundle mounts tool-skills with config.skills (as this repo's own behaviors/skills.yaml does), local dirs are silently never scanned, and there is no supported way for a user to override/customize a bundle-shipped skill (e.g. fork /council with a modified roster).

Evidence (from tool-skills source)

Paths relative to modules/tool-skills/amplifier_module_tool_skills/:

  • __init__.py:86-123_resolve_skill_sources() is a strict if/elif chain: when config["skills"] is present, get_default_skills_dirs() is never called. Default local-dir discovery is replaced, not merged. AMPLIFIER_SKILLS_DIR is dead for the same reason (discovery.py:437-460 only runs in the fallback branch).
  • discovery.py:392-434 (discover_skills_multi_source) — name collisions are strictly first-registered-wins (if name not in all_skills); no priority tiers.
  • __init__.py:596-603 — runtime load_skill(source=...) registration is session-memory only (no persistence anywhere), and on name collision the incumbent mount-time skill always wins, so it cannot be used to shadow either.
  • behaviors/skills.yaml:14-16 — this bundle's behavior sets config.skills to only the remote git source, despite its description mentioning workspace and user skills; combined with the above, user dirs are excluded from every session composed with this behavior.

Net effect: the documented priority table (workspace > user > bundle) only applies in sessions where no bundle configures skills — which excludes any session using this bundle's behavior. Users cannot shadow a bundle skill by name at all.

Workaround found (fragile)

Composition deep-merge concatenates list config parent-first (amplifier-foundation dicts/merge.py). A user can declare tool-skills with config.skills: ["/home/<user>/.amplifier/skills"] in their own behavior YAML and order it before the skills behavior in bundle.app in settings.yaml, making the local dir the first registered source. This works but relies on undocumented compose-order semantics and is per-user manual surgery.

Proposed fix

Have _resolve_skill_sources() always prepend get_default_skills_dirs() to configured sources instead of the current replace semantics — restoring the documented priority (workspace > user > env > bundle) and enabling intentional user-side skill shadowing. Alternatively: an explicit, documented settings surface for user skill sources with priority.

Drawbacks / considerations for the fix

  • Prepend-by-default enables silent shadowing: a stale local fork or accidental name collision quietly replaces bundle behavior in every session. Mitigation: log shadowing at mount, and have load_skill(info=...) report the shadowed path alongside the winner.
  • Fork skills spawn sub-sessions that re-resolve sources — the fix must apply consistently to spawned/delegated sessions or skill resolution diverges between parent and child.
  • At minimum, if replace semantics are kept, the docs (skills-instructions context, authoring guide, and this bundle's behavior description) should be corrected to say local dirs are NOT scanned when a bundle supplies config.skills.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions