Use a concise sentence-case title that describes the broken behavior.
Summary
In the Skills explorer (Connections → Explorer), built-in skills that are already installed (e.g. apple-notes, apple-reminders, findmy) render an Install button instead of an Installed state. Clicking it produces a red error toast — Could not install skill — skill already installed as "apple-notes" at /Users/<user>/.openhuman/skills/apple-notes — instead of being a no-op.
Problem
What happened: A registry tile for an already-installed built-in skill shows an active Install button. Clicking it fails with skill already installed as "<slug>" at <path> surfaced as an error toast.
What I expected: The tile should show Installed (disabled), and even if install is invoked for something already present, it should be an idempotent no-op / soft outcome — not a hard error.
Steps to reproduce:
- Open the app → Connections → Explorer tab (Skills explorer), Registry view.
- Locate a built-in skill that ships pre-installed at
~/.openhuman/skills/ (e.g. apple-notes).
- Observe the card shows Install (not Installed).
- Click Install → red error toast:
Could not install skill — skill already installed as "apple-notes" at /Users/<user>/.openhuman/skills/apple-notes.
Impact: Confusing, looks like a failure for normal pre-installed skills; users cannot tell what is already installed from the registry view. Low severity (no data loss), but it reads as a broken install flow.
Platform: macOS desktop (dev build), main. Likely all platforms.
Solution (optional)
Two independent defects, either of which would prevent the confusing error; ideally fix both:
-
Installed-state matching mismatch (frontend). In app/src/components/skills/SkillsExplorerTab.tsx, the tile is passed installed={installedIds.has(entry.id)} (line ~905), where installedIds = new Set(skills.map(s => s.id)) (line ~580). Installed skill id is the on-disk dir_name slug (e.g. apple-notes), but the catalog CatalogEntry.id is a registry/catalog-namespaced id that does not equal that slug for built-ins — so the lookup misses and the tile shows Install. Match on a stable shared key (normalized slug/name derived the same way derive_install_slug derives it) rather than entry.id.
-
Hard error on re-install (backend). src/openhuman/workflows/ops_install.rs:266 returns Err("skill already installed as {slug:?} at {}") when the target dir exists. The skill_registry_install path surfaces this as an error toast. Treat "already installed" as an idempotent success/no-op (return the existing install, or a soft info outcome) so re-invoking install is harmless.
CatalogTile already renders an Installed state when installed is true (SkillsExplorerTab.tsx:285), so fix #1 is purely making the match correct.
Acceptance criteria
Related
Use a concise sentence-case title that describes the broken behavior.
Summary
In the Skills explorer (Connections → Explorer), built-in skills that are already installed (e.g.
apple-notes,apple-reminders,findmy) render an Install button instead of an Installed state. Clicking it produces a red error toast —Could not install skill — skill already installed as "apple-notes" at /Users/<user>/.openhuman/skills/apple-notes— instead of being a no-op.Problem
What happened: A registry tile for an already-installed built-in skill shows an active Install button. Clicking it fails with
skill already installed as "<slug>" at <path>surfaced as an error toast.What I expected: The tile should show Installed (disabled), and even if install is invoked for something already present, it should be an idempotent no-op / soft outcome — not a hard error.
Steps to reproduce:
~/.openhuman/skills/(e.g.apple-notes).Could not install skill — skill already installed as "apple-notes" at /Users/<user>/.openhuman/skills/apple-notes.Impact: Confusing, looks like a failure for normal pre-installed skills; users cannot tell what is already installed from the registry view. Low severity (no data loss), but it reads as a broken install flow.
Platform: macOS desktop (dev build),
main. Likely all platforms.Solution (optional)
Two independent defects, either of which would prevent the confusing error; ideally fix both:
Installed-state matching mismatch (frontend). In
app/src/components/skills/SkillsExplorerTab.tsx, the tile is passedinstalled={installedIds.has(entry.id)}(line ~905), whereinstalledIds = new Set(skills.map(s => s.id))(line ~580). Installed skillidis the on-diskdir_nameslug (e.g.apple-notes), but the catalogCatalogEntry.idis a registry/catalog-namespaced id that does not equal that slug for built-ins — so the lookup misses and the tile shows Install. Match on a stable shared key (normalized slug/name derived the same wayderive_install_slugderives it) rather thanentry.id.Hard error on re-install (backend).
src/openhuman/workflows/ops_install.rs:266returnsErr("skill already installed as {slug:?} at {}")when the target dir exists. Theskill_registry_installpath surfaces this as an error toast. Treat "already installed" as an idempotent success/no-op (return the existing install, or a soft info outcome) so re-invoking install is harmless.CatalogTilealready renders an Installed state wheninstalledis true (SkillsExplorerTab.tsx:285), so fix #1 is purely making the match correct.Acceptance criteria
entry.id≠ on-disk slug) and Rust coverage for idempotent re-install.Related
app/src/components/skills/SkillsExplorerTab.tsx,app/src/services/api/skillRegistryApi.ts,src/openhuman/workflows/ops_install.rs.