Repository navigation
Fix symlinked orchestration skill coverage - #8329
Conversation
📝 WalkthroughWalkthroughAdded global skill discovery roots for Grok, OpenCode, Pi, Antigravity, and Cursor. Extended orchestration-skill location identifiers, path matching rules, and agent mappings for these providers. Added tests covering generated discovery paths and orchestration-skill detection in each provider-specific global directory. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Taking a look! |
d06ff5e to
8bd1b66
Compare
|
Thanks for the contribution! I rebased the branch onto current |
8bd1b66 to
e12c8b5
Compare
|
Rebased onto current \ and resolved the conflict in \ by keeping the newer agent-detection call shape together with the skill-source wiring from this PR. Force-pushed the updated branch. I couldn’t run the targeted Vitest/typecheck commands in this worktree because the checkout here does not have installed Node dependencies. |
The coverage widget dropped useDetectedAgents' detection target, which reads as "host not yet known": detectedIds pins to null, so loading never clears and the widget spins forever. It also reverted the remote-host scoping from stablyai#9790. - Restore the { kind: 'local' } target and assert it in the component test, whose useDetectedAgents mock previously ignored its arguments. - Cover the discovery contract renderer coverage consumes: every deduped rootPath must key an owning source, or that agent silently reads uncovered.
… root Coverage keyed sources into a Map by path, but one path can carry several sources: a workspace whose cwd is the home directory scans ~/.claude/skills and ~/.agents/skills as both home and repo roots. Sources are label-sorted, so the repo duplicate always landed last and won the Map, and its 'repo' sourceKind then excluded the root — every agent read Missing while the panel above still said Installed. WSL Settings hits this every time: with no requested cwd, resolveSkillDiscoveryTarget falls back to cwd = the distro home. Scan the sources for a matching owning root instead of keying by path. That also drops the Map and the two-function split, since the exported entry point is now the only implementation.
- owners must name real agent ids; AgentType widens to string, so a typo reads Missing forever without a type error - a home-equal workspace path must still yield both a home and a repo root, or the duplicate-root regression test asserts a shape that no longer exists - Claude plugin roots count for Claude, matching the Codex plugin cache
|
Thanks — the direction here is right, and I've pushed three fixes on top. Replacing the renderer's hardcoded path-segment table with the Confirmed the bug is real. With Three fixes pushed:
Behavior checked against Please rewrite the description before this merges — it becomes the squash commit message. It currently claims to add the Grok/OpenCode/Pi/Antigravity/Cursor discovery roots, but those landed in #8510 and this diff touches no discovery roots at all. Validated in a dev build: coverage resolves to "All 7 detected agents have the skill", and reverting fix (1) reproduces the permanent spinner. Live discovery on a symlink-heavy home returns |
stablyai#6422 adds an omp-home scan root plus an omp entry in the path-segment table this PR deletes. The root is what matters; owner: 'omp' already satisfies the new matcher, so re-express the coverage cases here and the rebase can drop that PR's coverage hunks with nothing lost. Also pins the Pi/OMP distinction stablyai#6422 guarded: both roots end in agent/skills, so a mis-mapped owner would cross-cover them.
Greptile SummaryThis PR replaces the hand-maintained
Confidence Score: 5/5Safe to merge — the change is entirely renderer-side display logic with no main-process production file changes and a comprehensive test suite. The rewrite correctly removes the hand-maintained path-segment allowlist and replaces it with a source-owner lookup that naturally handles symlink deduplication. The three new main-process discovery tests pin exactly the contract the renderer now relies on. The restored Files Needing Attention: No files require special attention.
|
| Filename | Overview |
|---|---|
| src/renderer/src/lib/orchestration-skill-coverage.ts | Core logic rewritten to use sources from discovery scan; removes brittle path-segment allowlist. Logic is clean, well-commented, and covers repo-root shadowing and multi-root deduplication correctly. |
| src/renderer/src/lib/orchestration-skill-coverage.test.ts | Comprehensive test updates; adds cases for symlink dedup, repo-root shadowing, home+repo duplicate path, unmatched sources, plugin roots, Windows paths, and all provider pairs. |
| src/renderer/src/hooks/useInstalledAgentSkills.ts | Minimal additive change: exposes sources from SkillDiscoveryResult via the existing resultForRender memoization, consistent with how skills is already plumbed. |
| src/renderer/src/components/settings/OrchestrationPane.tsx | Destructs sources from the hook and forwards it to OrchestrationSkillAgentCoverage; no logic changes. |
| src/renderer/src/components/settings/OrchestrationSkillAgentCoverage.tsx | Accepts the new sources prop, restores required { kind: 'local' } to useDetectedAgents, and forwards sources to getOrchestrationSkillAgentStatuses. |
| src/renderer/src/components/settings/OrchestrationSkillAgentCoverage.test.tsx | Mock promoted to a named spy; test now asserts useDetectedAgents receives { kind: 'local' }, catching the dropped-argument regression that caused an infinite spinner. |
| src/renderer/src/components/settings/OrchestrationPane.test.tsx | Mock updated to include the new sources field; change is mechanical and correct. |
| src/main/skills/discovery.test.ts | Three new integration tests pin the discovery contract the renderer depends on; symlink dedup, agent-id validity, and home-equal-cwd dual-root shape are all covered. |
Reviews (2): Last reviewed commit: "Merge main (#6422 OMP discovery) into sy..." | Re-trigger Greptile
stablyai#6422 added an omp-home scan root plus an omp entry in the path-segment table this branch deletes. Kept its skill-discovery-sources.ts root in full — without it ~/.omp/agent/skills is never scanned. Dropped its orchestration-skill-coverage.ts and .test.ts hunks: the tables are gone, and owner: 'omp' already satisfies the owner-based matcher. Both of its coverage concerns are carried here in the new shape, including the Pi/OMP anchor guard.
|
Settings → Orchestration → Agent coverage decided whether each detected agent had the orchestration skill by matching a skill's single `rootPath` against a hand-maintained table of path segments. Skill discovery dedups by canonical file path, so when one provider home is a symlink onto another agent's skills directory — which `npx skills add --global` does — the two collapse into one row and the absorbed roots survive only in `rootPaths`, which the old matcher ignored. With `~/.grok/skills` symlinked onto `~/.claude/skills`, Claude read Ready and Grok read Missing even though Grok loads the same files. Now classifies every entry in `rootPaths` and resolves each root through the `SkillDiscoverySource` the scanner already returns, accepting it when `sourceKind` is not `repo` and `owner` is either the agent's owner or `null`. Deletes `ORCHESTRATION_SKILL_LOCATIONS` and `ORCHESTRATION_SKILL_LOCATION_IDS_BY_AGENT` — a renderer-side mirror of `buildSkillDiscoverySources` that had to be hand-edited for every new provider root, which is what caused this class of bug. No discovery roots change here; those landed in stablyai#8510. Fixed during review: - Restored the required `{ kind: 'local' }` argument to `useDetectedAgents`. Dropping it failed typecheck (TS2554) and pinned `detectedIds` to null, leaving the widget on "Checking installed agents and skill paths…" forever. It also reverted the remote-host scoping from stablyai#9790. - Stopped a duplicate repo root from shadowing an owning home root. `SkillDiscoverySource.path` is not unique: when the scan cwd is the home directory, `~/.claude/skills` is scanned as both a home root and a repo root, and a path-keyed Map is last-write-wins — so every agent read Missing while the panel above said Installed. Guaranteed in Settings on a WSL project. - Carried OMP coverage in the owner-based shape so stablyai#6422's coverage-table hunks could be dropped on merge. Behavior note: an orchestration skill inside an enabled Claude plugin now counts for Claude. That is a deliberate consequence of trusting the scanner's ownership data, matches how the Codex plugin cache was already treated, and is pinned by a test. Refs stablyai#8256 Co-authored-by: sdhilip200 <49802211+sdhilip200@users.noreply.github.com>
Summary
Settings → Orchestration → Agent coverage decided whether each detected agent had the orchestration skill by matching a skill's single
rootPathagainst a hand-maintained, renderer-side table of path segments (.claude/skills,.grok/skills,.gemini/antigravity/skills, …).Skill discovery dedups skills by canonical file path. When one provider home is a symlink onto another agent's skills directory —
npx skills add --globaldoes exactly this — the two collapse into a single row and the absorbed roots survive only inrootPaths, which the old matcher ignored. With~/.grok/skillssymlinked onto~/.claude/skills, discovery returns one row withrootPath = ~/.claude/skills, so Claude read Ready and Grok read Missing even though Grok loads the very same files.rootPaths, not just the root that happened to win dedupSkillDiscoverySourcethe scanner already returns, accepting it when itssourceKindis notrepoand itsowneris either the agent's owner ornull(the shared~/.agents/skillsscope)ORCHESTRATION_SKILL_LOCATIONSandORCHESTRATION_SKILL_LOCATION_IDS_BY_AGENT— a renderer-side mirror ofbuildSkillDiscoverySourcesthat had to be hand-edited for every new provider root, which is what caused this class of bug in the first place (orchestration-skill-coverage.ts: 197 → 89 lines)sourcesfromuseInstalledAgentSkill→OrchestrationPane→OrchestrationSkillAgentCoverageNo discovery roots change here. The Grok/OpenCode/Pi/Antigravity/Cursor scan roots already landed in #8510; this PR touches no main-process production file. It fixes the coverage half that #8510 left behind.
User-visible changes
~/.agents/skillsonly ·~/.claude/skillsonly · repo-local.agents/skills· Codex plugin cache · Gemini vs Antigravity · agents with no dedicated rootThe Claude plugin flip is a deliberate consequence of trusting the scanner's ownership data: Claude Code does load skills from enabled plugins, and the Codex plugin cache was already treated this way. Excluding it would mean reintroducing the allowlist this PR removes. It is only reachable in Settings on a WSL project, since the native Settings path sends no
cwdand plugin roots arecwd-gated. Pinned by a test.Fixes made during review
{ kind: 'local' }argument touseDetectedAgents. Dropping it failedpnpm typecheck(TS2554), and at runtime an absent target means "host not yet known", pinningdetectedIdstonullso the widget sat on "Checking installed agents and skill paths…" forever. It also reverted the remote-host scoping from fix(agents): scope Settings agent list and quick-launch menu to the remote-server host #9790. The component test's mock ignored its arguments, so nothing caught it; it now asserts the target.SkillDiscoverySource.pathis not unique: when the scan's cwd is the home directory,~/.claude/skillsand~/.agents/skillsare each scanned as both a home root and a repo root. Indexing sources into a path-keyedMapis last-write-wins, andsourcesis label-sorted, soRepo … .claudealways won and itsrepokind then excluded the root — every agent read Missing while the panel directly above still said Installed. This was guaranteed in Settings on a WSL project, whereresolveSkillDiscoveryTargetdefaultscwdto the distro home. Coverage now scans for a matching owning root instead of keying by path.rootPathmust key an owning source, source owners must name real agent ids (AgentTypewidens tostring, so a typo would read Missing forever with no type error), and a home-equal workspace path must still yield both a home and a repo root.Refs #8256
Testing
pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/orchestration-skill-coverage.test.ts src/renderer/src/components/settings/OrchestrationSkillAgentCoverage.test.tsx src/renderer/src/components/settings/OrchestrationPane.test.tsx src/main/skills/discovery.test.ts— 38 passedsrc/main/skills,settings,hooks,native-chat,libpnpm typecheck,pnpm lintuseDetectedAgentsfix reproduces the permanent spinner. Live discovery on a symlink-heavy home returnsrootPathsspanning.agents,.claude,.grok, and.pi, each resolving to a correctly-owned source.discoverSkills: with~/.grok/skillssymlinked onto~/.claude/skills,maingivesclaude: true/grok: false; this branch covers both.Original fix by @sdhilip200; review fixes pushed on top.