Skip to content

Skills explorer shows Install (not Installed) for pre-installed built-in skills and errors on click #3585

Description

@graycyrus

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:

  1. Open the app → Connections → Explorer tab (Skills explorer), Registry view.
  2. Locate a built-in skill that ships pre-installed at ~/.openhuman/skills/ (e.g. apple-notes).
  3. Observe the card shows Install (not Installed).
  4. 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:

  1. 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.

  2. 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

  • Repro gone — Pre-installed built-in skills show Installed (disabled) in the registry view; invoking install for an already-installed skill no longer surfaces an error toast.
  • Regression safety — Vitest coverage for installed-state matching of built-ins (catalog entry.id ≠ on-disk slug) and Rust coverage for idempotent re-install.
  • Diff coverage ≥ 80% — the fix PR meets the changed-lines coverage gate.
  • Installed↔Registry parity — the Installed view and Registry view agree on what is installed.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions