Repository navigation
Conversation
…roles For a member with no role and no active project on a team that has manifest/projects.yaml, resolveDesiredSkills returned an empty set: scanRoleAwareSkills only scanned namespace directories and never the shared skills/<name>/ root. Root skills stopped deploying, and the desired-union cleanup then pruned every root skill already on disk — real teams lost their whole shared skill set on the next pull. Root skills are shared per the usage guide and the rules/agents resolvers: scanRoleAwareSkills now seeds the root SKILL.md directories before the active namespaces, so an active same-name namespace entry replaces the root one whole while every member keeps the shared copy. buildRolePullContext also counts root names as active so a conflict run cannot prune a root skill that is byte-identical to an inactive namespace copy. The now-unreachable 'root skills arrive only through a tag' hint (Tencent#911) is removed; tags can still pull a skill out of a namespace the member has not activated.
|
Findings
The PR description includes sufficient unit, E2E, and representative real-CLI verification. |
Collaborator
|
Please resolve the conflicts. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
For a member with no role and no active project on a team that has
manifest/projects.yaml,resolveDesiredSkillsreturned an empty set:scanRoleAwareSkillsonly scanned namespace directories and never the sharedskills/<name>/root. Two consequences on every pull:No resources to synceven with--force).pull.tsthen pruned every root skill already on disk from every AI tool — in production we watched a team's entire shared root skill set (51 skills) disappear from all clients on the pull aftermanifest/projects.yamlfirst landed. Only skills with unpushed local edits survived, via the safety gate.Root cause vs. prior design
This behavior was introduced deliberately in #911/#917 ("root = tag catalog: while the team uses roles or projects, root skills arrive only through a tag"), but it contradicts:
docs/usage-guide.md(namespace-override chapter): "Put shared content … at the root … everyone else keeps the shared one."src/resource-namespaces.ts's own comment: deactivating projects must "scope down to role-only + shared resources".rulesandagentsresolvers, which both keep root items and let an active namespace replace them by name.This PR unifies skills with the documented model: root skills are shared with every member; an active same-name namespace entry replaces the root one whole.
Semantic change to call out:
tags subscribeno longer has an effect on root skills (they are always delivered); tags can still pull a skill out of a namespace the member has not activated. Docs updated accordingly in both languages.Changes
src/resources/desired.tsscanRoleAwareSkills: seed rootskills/<name>/directories (withSKILL.md) before scanning the active namespaces;resolveNamespacedItemsthen applies the existing "namespace replaces root" semantics.buildRolePullContext: count root skill names as active, so a conflict run (which falls back toactiveSkillNames) cannot prune a root skill that is byte-identical to an inactive-namespace copy.src/pull.ts: drop the now-unreachable "root skills arrive only through a tag" hint and its tracking vars ([bug] pull removes root skills without a word when roles or projects take effect #911 wording is no longer true).desired-skills.test.ts(empty-active-namespaces member still receives root skills; namespace replaces root with exactoverridesreport) andpull-namespace-override.test.ts(pull-level: root skills land, survive a second pull, namespace skill arrives afterprojects set, leaves after deactivation while root stays). Seven existing tests that encoded the old "root = tag catalog" semantics were rewritten to assert the documented semantics; no still-valid coverage was removed.docs/usage-guide.md+.zh-CN.md,skill-data/setup/references/manage-admin.mdsynced to the unified semantics.Testing
npx tsc --noEmit,npm run lint— clean.npx vitest run desired-skills pull-namespace-override pull-tombstone pull-project-cleanup— 67/67.npx vitest run— 8131 passed / 1 failed / 30 skipped; the single failure (uninstall.test.ts"removes the separate team-hook files of live bare worktrees") fails in setup because it needs git ≥ 2.42 forgit worktree add --orphanand this machine has 2.39.5 — environment-only, unrelated.npm run test:e2e -- multi-project roles-tags-pull namespaced-entries project-scoped-delivery— 12/12.Real-CLI verification (before/after, built dist)
Sandbox: seed repo with
skills/root-skill,skills/aivoice/proj-skill,manifest/projects.yaml(declaresaivoice),teamai.yaml; bare remote viaurl.insteadOf;HOME=/tmp/e2e-rootfix/home.Before (unfixed build — bug reproduced):
init --scope user --provider git→No resources to sync;root-skillabsent from.claude/skills/.root-skillcopy,pull --force→Removed 1 skill(s) no longer delivered here: root-skill. While the team uses roles or projects, root skills arrive only through a tag…— the data-loss path.After (this PR, same sandbox reset):
init→Synced 1 skills (1 new);~/.claude/skills/root-skill/SKILL.mdpresent,proj-skillabsent.pull --force→ root skill kept, not cleaned.teamai projects set aivoice+pull --force→Synced 2 skills (1 new, 1 updated); bothproj-skillandroot-skillon disk.teamai projects set(deactivate) +pull --force→Removed 1 skill(s) no longer delivered here: proj-skill.(no stale tag hint);root-skillstill on disk — namespace cleanup semantics intact.