You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Files / modules: src/quickapp/skills/_skills_registry.py, src/quickapp/skills/skills_provider.py, src/quickapp/skill_invocation/_invoked_skills_context.py, src/quickapp/dial_skills/ (DialSkillResolver's name dedup).
Current behaviour: a skill is keyed by the name in its own manifest. SkillsRegistry merges every SkillsProvider sorted by order (lower wins) and drops the loser of a name collision, reporting it in the "Initialization issues" stage. AgentSkillsProvider is order = 0; skills a user invoked from a message (Invoke user skills from a chat message (phase 1) #549) are order = -10, so they win. Among several invoked skills that share a manifest name, DialSkillResolver's name dedup keeps the oldest pick and drops the rest.
How it got this way: name-keying predates user-supplied skills, where all skills came from the app author and a collision was an authoring mistake. Skill invocation (Invoke user skills from a chat message (phase 1) #549) added a second, user-controlled source under the same key, and the design doc (docs/designs/skill_invocation.md, "A picked skill shadows the agent's same-named skill") accepted the collision explicitly, on the stated assumption that users do not invoke skills whose names clash with the agent's. Nothing enforces the assumption. A collision-free naming scheme was sketched there as a follow-up.
Why is this debt?
Impact today: a user who invokes their own code-review silently loses the agent's code-review for that conversation — including a predefined skill the app author considers part of the product. The app author cannot prevent it, and the user is unlikely to connect the "Initialization issues" note to the behaviour they then see. Two of a user's own skills sharing a name cannot both be used in one conversation.
Risk if left unaddressed: the assumption gets less true the more skills users write. code-review, pr-description and report-style are exactly the names both an app author and a user pick.
Features it blocks: loading all of a user's skills via Core dependencies, where a collision stops being a deliberate clashing pick and becomes ordinary for anyone whose bucket holds a common name; and invoking several skills from one message, where two picks sharing a name is a case a user can hit without trying.
Proposed remediation
Approach: give every skill an identity that cannot collide, and keep the display name separate from it. A skill's resource path is already unique (skills/<bucket>/code-review), and predefined and app skills have their own stable identity, so the key can stop being the manifest name. What the model and the user see stays readable — code-review for the agent's, code-review (my skills) or similar for the user's — so neither reads an internal identifier. With no possible collision, SkillsProvider.order stops deciding anything.
Affected modules: skills/_skills_registry.py (key and merge), skills/_xml.py (how a name is listed), the read_skill tool's lookup, skill_invocation/ (the synthetic call's skill_name argument and its stage title), DialSkillResolver (name dedup can be switched off once names cannot clash).
Also worth settling here: an invoked skill currently contributes its whole manifest — description, license, compatibility, allowed-tools, arbitrary metadata — into the app author's system prompt, with the description unbounded. Once a user skill is listed under a name of QuickApps' making, that copy should be trimmed and the description capped.
Migration: conversations already in flight keep the name their skills were loaded under, so an invoked skill's bundled files stop resolving in those conversations. Acceptable while invocation is preview-gated.
Alternatives considered
Leave it as is and rely on the non-clashing-names assumption: the current state. Cheap, and invisible until someone loses a product skill.
Let the agent's skill win instead (order = 30): worse. The synthetic read_skill addresses the skill by name, so the user's explicit pick would load the agent's skill — the wrong content on the one action the user took deliberately.
Spare predefined skills but not app-declared ones (order = 5): splits the rule in two without removing the clash.
Reject an invoked skill whose name clashes: honest but useless to the user, who cannot rename a shared or published skill.
Additional information
Prerequisite for #569 (loading the user's skills via Core dependencies) and related to #567 (several invoked skills per message). Phase 1a design and the accepted-collision discussion: docs/designs/skill_invocation.md. Built on #549.
QuickApps version
latest
Area
Skills
What is the current state?
src/quickapp/skills/_skills_registry.py,src/quickapp/skills/skills_provider.py,src/quickapp/skill_invocation/_invoked_skills_context.py,src/quickapp/dial_skills/(DialSkillResolver's name dedup).namein its own manifest.SkillsRegistrymerges everySkillsProvidersorted byorder(lower wins) and drops the loser of a name collision, reporting it in the "Initialization issues" stage.AgentSkillsProviderisorder = 0; skills a user invoked from a message (Invoke user skills from a chat message (phase 1) #549) areorder = -10, so they win. Among several invoked skills that share a manifest name,DialSkillResolver's name dedup keeps the oldest pick and drops the rest.docs/designs/skill_invocation.md, "A picked skill shadows the agent's same-named skill") accepted the collision explicitly, on the stated assumption that users do not invoke skills whose names clash with the agent's. Nothing enforces the assumption. A collision-free naming scheme was sketched there as a follow-up.Why is this debt?
code-reviewsilently loses the agent'scode-reviewfor that conversation — including a predefined skill the app author considers part of the product. The app author cannot prevent it, and the user is unlikely to connect the "Initialization issues" note to the behaviour they then see. Two of a user's own skills sharing a name cannot both be used in one conversation.code-review,pr-descriptionandreport-styleare exactly the names both an app author and a user pick.Proposed remediation
skills/<bucket>/code-review), and predefined and app skills have their own stable identity, so the key can stop being the manifest name. What the model and the user see stays readable —code-reviewfor the agent's,code-review (my skills)or similar for the user's — so neither reads an internal identifier. With no possible collision,SkillsProvider.orderstops deciding anything.skills/_skills_registry.py(key and merge),skills/_xml.py(how a name is listed), theread_skilltool's lookup,skill_invocation/(the synthetic call'sskill_nameargument and its stage title),DialSkillResolver(name dedup can be switched off once names cannot clash).description,license,compatibility,allowed-tools, arbitrarymetadata— into the app author's system prompt, with the description unbounded. Once a user skill is listed under a name of QuickApps' making, that copy should be trimmed and the description capped.Alternatives considered
order = 30): worse. The syntheticread_skilladdresses the skill by name, so the user's explicit pick would load the agent's skill — the wrong content on the one action the user took deliberately.order = 5): splits the rule in two without removing the clash.Additional information
Prerequisite for #569 (loading the user's skills via Core dependencies) and related to #567 (several invoked skills per message). Phase 1a design and the accepted-collision discussion:
docs/designs/skill_invocation.md. Built on #549.