Skip to content

fix(skills): detect installed built-in catalog entries - #3656

Merged
senamakel merged 8 commits into
tinyhumansai:mainfrom
YonganZhang:fix/3585-installed-builtins
Jun 22, 2026
Merged

fix(skills): detect installed built-in catalog entries#3656
senamakel merged 8 commits into
tinyhumansai:mainfrom
YonganZhang:fix/3585-installed-builtins

Conversation

@YonganZhang

@YonganZhang YonganZhang commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • match registry catalog entries against installed skills using normalized install keys, so built-in entries with catalog ids like built-in/apple-notes render as Installed when the local slug is apple-notes
  • apply the same installed check in the detail dialog so duplicate installs are not offered there
  • make URL-based skill installation idempotent when the target SKILL.md already exists, returning a no-op success instead of surfacing a red error toast

Closes #3585.

Tests

  • cargo fmt --check
  • git diff --check

Not run

  • pnpm --dir app test -- SkillsExplorerTab.test.tsx (blocked locally: app/node_modules is missing; current Node is v22.22.1 while package requires >=24)
  • cargo test install_workflow_from_url_is_idempotent_when_skill_already_exists (blocked locally: missing system pkg-config dependencies alsa.pc and xi.pc)

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved Skills Explorer “installed” matching so the correct Installed badge and install-button availability are shown.
    • Made workflow installation idempotent when SKILL.md already exists, avoiding unnecessary failures and overwrites.
  • Tests

    • Updated Skills Explorer UI tests to validate the revised installed-state behavior using scoped assertions.
    • Added a new test to confirm idempotent install outcomes.
  • Chores

    • Improved web E2E/Rust E2E scripts to dynamically locate rustc/cargo before building and running tests.

@YonganZhang
YonganZhang requested a review from a team June 13, 2026 22:31
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes installed-skill detection for built-in catalog entries in the skills explorer and changes workflow URL installs to succeed as a no-op when the target skill is already present, with matching frontend and Rust test updates. Build scripts are updated to discover cargo and rustc bin paths dynamically instead of assuming they are on PATH.

Changes

Skills install parity

Layer / File(s) Summary
Frontend install-state detection implementation
app/src/components/skills/SkillsExplorerTab.tsx
The explorer now derives normalized install keys from catalog entries and installed workflows instead of comparing raw IDs. New utilities (slugifyInstallKey, catalogInstallKeys, workflowInstallKeys, isCatalogEntryInstalled) extract and match stable keys via Set overlap. The installedKeys memo replaces installedIds. Registry tiles and the detail dialog use isCatalogEntryInstalled(entry, installedKeys) to determine installed state and install-button availability.
Frontend installed-state tests
app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx
Tests verify that catalog entries and installed workflows are correctly matched by normalized keys. Tests use the built-in/apple-notes catalog entry paired with an apple-notes installed workflow, asserting that the Installed badge is shown in the matching tile and that unrelated installed skills do not trigger false positives. Scoped DOM assertions use within to verify badge and control presence within each tile.
Backend idempotent install outcome
src/openhuman/workflows/ops_install.rs
install_workflow_from_url delegates to a new install_workflow_from_url_with_home helper with explicit home-path threading. When the target directory exists, the function checks for SKILL.md; if present, returns a successful result with no new skills and an "already installed" message; collisions without SKILL.md remain errors. Both pre- and post-install workflow discovery use the passed home path.
Backend idempotency test infrastructure
src/openhuman/workflows/ops_tests.rs
A new EnvVarGuard utility captures and restores environment variables on drop. A new async test uses WireMock to serve synthetic SKILL.md, temporarily enables local HTTP via the guarded env var, installs the same workflow URL twice, and verifies the second run yields no new skills and reports already installed.

Build toolchain resolution

Layer / File(s) Summary
Toolchain bin path resolution in build scripts
app/scripts/e2e-web-build.sh, scripts/test-rust-e2e.sh
Both scripts resolve RUSTC_BIN via command -v rustc and derive CARGO_BIN from an optional override env var or from the rustc directory (with fallback to command -v cargo). RUST_HOST_TRIPLE is computed using the resolved rustc binary. Build invocations use the resolved $CARGO_BIN variable instead of calling cargo directly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Sequence Diagrams

sequenceDiagram
  participant CatalogRegistry
  participant SkillsExplorer
  participant InstallKeyUtils
  participant InstalledWorkflows
  CatalogRegistry->>SkillsExplorer: catalog entries
  InstalledWorkflows->>SkillsExplorer: installed workflows
  SkillsExplorer->>InstallKeyUtils: catalogInstallKeys(entry)
  SkillsExplorer->>InstallKeyUtils: workflowInstallKeys(workflow)
  InstallKeyUtils->>SkillsExplorer: normalized keys (Set)
  SkillsExplorer->>InstallKeyUtils: isCatalogEntryInstalled(entry, keys)
  InstallKeyUtils->>SkillsExplorer: boolean (installed state)
  SkillsExplorer->>CatalogRegistry: render tile with installed state
Loading
sequenceDiagram
  participant Client
  participant install_workflow_from_url
  participant install_workflow_from_url_with_home
  participant FileSystem
  participant WorkflowDiscovery
  Client->>install_workflow_from_url: install request
  install_workflow_from_url->>install_workflow_from_url_with_home: workspace_dir, params, home
  install_workflow_from_url_with_home->>WorkflowDiscovery: discover before (with home)
  install_workflow_from_url_with_home->>FileSystem: check target directory
  alt SKILL.md exists
    FileSystem-->>install_workflow_from_url_with_home: file found
    install_workflow_from_url_with_home-->>install_workflow_from_url: Ok(already installed, no new skills)
  else no SKILL.md but dir exists
    FileSystem-->>install_workflow_from_url_with_home: file not found
    install_workflow_from_url_with_home-->>install_workflow_from_url: Err(collision)
  else dir does not exist
    FileSystem-->>install_workflow_from_url_with_home: not found
    install_workflow_from_url_with_home->>WorkflowDiscovery: discover after (with home)
    install_workflow_from_url_with_home-->>install_workflow_from_url: Ok(new skills)
  end
  install_workflow_from_url-->>Client: outcome
Loading

Possibly related PRs

  • tinyhumansai/openhuman#3722: Both PRs modify src/openhuman/workflows/ops_install.rs's install_workflow_from_url logic; this PR adds idempotent handling when SKILL.md already exists, while that PR adds DomainEvent::WorkflowsChanged emission for mid-session refresh.

Suggested reviewers

  • sanil-23
  • oxoxDev

Poem

🐇 I sniffed a skill beside the tree,
"Installed already!" it said to me.
No grumpy toast, no second thump,
just tidy keys and a softer jump.
In burrow logs, all calm and bright,
the same old skill now lands just right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(skills): detect installed built-in catalog entries' directly summarizes the core objective of fixing built-in skill detection in the Skills explorer, aligning with the main issue #3585.
Linked Issues check ✅ Passed The PR addresses both core requirements: (1) built-in skill detection via normalized install keys in SkillsExplorerTab, and (2) idempotent installation in ops_install.rs that returns success when SKILL.md exists. Test coverage includes specific built-in entries, install-key matching logic, and re-install idempotency scenarios.
Out of Scope Changes check ✅ Passed All changes directly support the primary objectives: skill detection logic, installation idempotency, test coverage, and necessary build script improvements for testing environments. No unrelated refactoring or feature additions detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team. labels Jun 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 433962edc2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

function workflowInstallKeys(skill: WorkflowSummary): string[] {
return [
slugifyInstallKey(skill.id),
slugifyInstallKey(skill.name),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not match installs by display name

Including skill.name in installedKeys means an unrelated local or project skill with the same frontmatter display name as a catalog entry is treated as installed even when its on-disk slug/id is different. For example, a user-created skill with id: apple-notes-copy and name: Apple Notes will hide the Install action for built-in/apple-notes, though the registry installer would create ~/.openhuman/skills/apple-notes; compare only canonical slugs/path-derived keys or otherwise verify the directory slug.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/openhuman/workflows/ops_install.rs (1)

271-296: ⚡ Quick win

Update install Rustdoc to reflect idempotent existing-directory behavior.

The runtime now returns success when <slug>/SKILL.md already exists, but the function contract text above still states collision is fatal/no silent overwrite. Aligning docs with behavior will prevent caller/test assumptions from drifting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/workflows/ops_install.rs` around lines 271 - 296, The Rustdoc
comments for the install_workflow_from_url function need to be updated to
reflect the idempotent behavior implemented in the code. Currently, the
documentation describes collision as fatal with no silent overwrite, but the
actual implementation now returns success (Ok outcome) when the target directory
already exists with a valid SKILL.md file. Update the function's documentation
comments to describe this idempotent behavior: that the function safely handles
the case where the skill is already installed by returning success with
appropriate messaging, rather than treating it as a fatal error condition.
app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx (1)

305-335: ⚡ Quick win

Add one assertion for dialog-level installed parity in this built-in case.

This test validates tile state, but not the updated detail-dialog installed gating path. After opening the tile, assert the dialog does not render an Install action for the already-installed built-in entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx` around lines
305 - 335, The test for the "Installed" badge only validates tile-level state
but does not check the detail dialog. After the existing assertions that verify
the Installed badge is shown in the tile and the install button is not present
in the tile, add code to click or open the tile to display the detail dialog,
then add an assertion to verify that the Install action is also not rendered in
the dialog for the already-installed built-in entry. You can use the same
pattern as the existing tile assertions, checking that within the dialog
context, the registry-install-built-in/apple-notes testId is not present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/skills/SkillsExplorerTab.tsx`:
- Around line 51-66: The install-key derivation in SkillsExplorerTab’s
catalogInstallKeys and workflowInstallKeys is too broad because it uses
entry.name and skill.name, which can cause unrelated items with the same display
name to be treated as already installed. Remove the name-based keys and keep
only stable identifiers sourced from entry.id, lastPathSegment(entry.id),
parentPathSegment(entry.docs_path), parentPathSegment(entry.download_url), and
skill.id / parentPathSegment(skill.location), so install-state matching relies
on unique, stable slug/id fields only.

---

Nitpick comments:
In `@app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx`:
- Around line 305-335: The test for the "Installed" badge only validates
tile-level state but does not check the detail dialog. After the existing
assertions that verify the Installed badge is shown in the tile and the install
button is not present in the tile, add code to click or open the tile to display
the detail dialog, then add an assertion to verify that the Install action is
also not rendered in the dialog for the already-installed built-in entry. You
can use the same pattern as the existing tile assertions, checking that within
the dialog context, the registry-install-built-in/apple-notes testId is not
present.

In `@src/openhuman/workflows/ops_install.rs`:
- Around line 271-296: The Rustdoc comments for the install_workflow_from_url
function need to be updated to reflect the idempotent behavior implemented in
the code. Currently, the documentation describes collision as fatal with no
silent overwrite, but the actual implementation now returns success (Ok outcome)
when the target directory already exists with a valid SKILL.md file. Update the
function's documentation comments to describe this idempotent behavior: that the
function safely handles the case where the skill is already installed by
returning success with appropriate messaging, rather than treating it as a fatal
error condition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 89891647-1656-41d7-a499-69c7125b821c

📥 Commits

Reviewing files that changed from the base of the PR and between 6ceee18 and 433962e.

📒 Files selected for processing (4)
  • app/src/components/skills/SkillsExplorerTab.tsx
  • app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx
  • src/openhuman/workflows/ops_install.rs
  • src/openhuman/workflows/ops_tests.rs

Comment thread app/src/components/skills/SkillsExplorerTab.tsx
@YonganZhang

Copy link
Copy Markdown
Contributor Author

Updated in fc0cbaa: removed display-name based installed matching, added a same-name/different-slug regression test, added dialog-level installed gating coverage, and updated install Rustdoc for idempotent already-installed behavior.\n\nValidation: cargo fmt --check passed; git diff --check passed. Local Vitest could not run because node_modules/vitest are not installed and this machine is on Node 22 while the app requires Node >=24.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 14, 2026
@Felyx-Fu

Copy link
Copy Markdown
Contributor

I opened a helper PR with fixes for the current CI failures: YonganZhang#2

What it changes:

  • Adds the missing install_workflow_from_url_with_home import for the new workflow idempotency test.
  • Updates skill_registry_e2e_sources_browse_search_install to expect duplicate installs to be idempotent success (new_skills empty + stdout mentions already installed), matching the branch behavior.

Validation run locally:

  • cargo fmt --manifest-path Cargo.toml --check
  • git diff --check -- src/openhuman/workflows/ops_tests.rs tests/skill_registry_e2e.rs

I also attempted the two targeted Rust tests locally, but both timed out during Windows Rust compilation after 240s, so CI is the authoritative verification for those.

@senamakel senamakel self-assigned this Jun 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3e4526df6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return [
slugifyInstallKey(entry.id),
slugifyInstallKey(lastPathSegment(entry.id)),
slugifyInstallKey(parentPathSegment(entry.docs_path)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not use docsPath categories as install keys

For Hermes entries, docs_path is shaped like bundled/<category>/<category>-<skill> (the parser test for apple-notes uses bundled/apple/apple-apple-notes), so parentPathSegment(entry.docs_path) adds only the category (apple, devops, etc.) to the catalog's install keys. If a user has an installed/local workflow whose slug is one of those categories, every registry entry in that category is treated as installed and the Install action/detail button is hidden even though the specific skill is absent; derive the skill slug from the last docsPath segment (stripping the category prefix) or avoid this key.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot added the agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. label Jun 22, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14a48b1e79

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

slugifyInstallKey(entry.id),
slugifyInstallKey(lastPathSegment(entry.id)),
slugifyInstallKey(parentPathSegment(entry.docs_path)),
slugifyInstallKey(parentPathSegment(entry.download_url)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid deriving installed state from download URL parents

For catalog entries whose download_url is not guaranteed to be <slug>/SKILL.md, this adds unrelated path components as install keys. For example, download_url_from_source_url can turn a GitHub root blob into https://raw.githubusercontent.com/org/repo/main/SKILL.md (src/openhuman/skill_registry/ops.rs:570-575), so parentPathSegment(entry.download_url) becomes main; any user with an unrelated installed skill slug main will see that catalog entry as Installed and lose both install buttons. Restrict this key to URL shapes known to encode the skill slug, or avoid matching on the download URL parent.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test-rust-e2e.sh`:
- Around line 141-144: The CARGO_BIN variable is initialized with a fallback
mechanism at lines 131-135, but there is no validation to ensure it is actually
set and executable if all discovery methods fail. Add an explicit error check
immediately after line 135 that verifies CARGO_BIN is not empty and is
executable, and if the check fails, exit with a clear error message that helps
users understand that cargo/rustc discovery failed. This will prevent confusing
errors when CARGO_BIN is later used in the bash invocations at lines 141 and
144.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e3b89f58-9018-442c-beab-4e1ad77e1807

📥 Commits

Reviewing files that changed from the base of the PR and between 14a48b1 and ac3f55c.

📒 Files selected for processing (3)
  • app/.prettierignore
  • app/scripts/e2e-web-build.sh
  • scripts/test-rust-e2e.sh
✅ Files skipped from review due to trivial changes (1)
  • app/.prettierignore
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/scripts/e2e-web-build.sh

Comment thread scripts/test-rust-e2e.sh
Comment on lines +141 to +144
bash "$SCRIPT_DIR/ci-cancel-aware.sh" "$CARGO_BIN" test --manifest-path Cargo.toml --test "$suite" -- "${EXTRA_ARGS[@]}"
else
echo "[rust-e2e] cargo test --manifest-path Cargo.toml --test $suite"
"$SCRIPT_DIR/ci-cancel-aware.sh" cargo test --manifest-path Cargo.toml --test "$suite"
echo "[rust-e2e] $CARGO_BIN test --manifest-path Cargo.toml --test $suite"
bash "$SCRIPT_DIR/ci-cancel-aware.sh" "$CARGO_BIN" test --manifest-path Cargo.toml --test "$suite"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify CARGO_BIN initialization and error handling in the E2E test script

# Show the CARGO_BIN resolution logic (lines 131-144)
echo "=== CARGO_BIN resolution logic ==="
sed -n '131,144p' scripts/test-rust-e2e.sh

echo ""
echo "=== Checking for SCRIPT_DIR definition ==="
rg -n 'SCRIPT_DIR=' scripts/test-rust-e2e.sh | head -5

echo ""
echo "=== Checking for error handling when cargo/rustc not found ==="
rg -n -A2 -B2 'CARGO_BIN.*command -v|rustc.*not found|cargo.*not found' scripts/test-rust-e2e.sh

Repository: tinyhumansai/openhuman

Length of output: 1122


Add error handling if cargo/rustc discovery fails.

CARGO_BIN is properly initialized with a multi-step fallback (lines 131-135): env var override → rustc directory → command -v cargo, and SCRIPT_DIR is correctly defined at line 26. However, if both rustc and cargo discovery fail, CARGO_BIN could be empty when passed to the cargo invocations (lines 141, 144), resulting in a confusing error message. Add an explicit check after line 135 to fail with a clear error message if CARGO_BIN is not set or executable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test-rust-e2e.sh` around lines 141 - 144, The CARGO_BIN variable is
initialized with a fallback mechanism at lines 131-135, but there is no
validation to ensure it is actually set and executable if all discovery methods
fail. Add an explicit error check immediately after line 135 that verifies
CARGO_BIN is not empty and is executable, and if the check fails, exit with a
clear error message that helps users understand that cargo/rustc discovery
failed. This will prevent confusing errors when CARGO_BIN is later used in the
bash invocations at lines 141 and 144.

@senamakel
senamakel merged commit ccee419 into tinyhumansai:main Jun 22, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

3 participants