Skip to content

Split skills registry and runtime modules#3481

Merged
senamakel merged 34 commits into
tinyhumansai:mainfrom
senamakel:feat/skill-registry-backends
Jun 8, 2026
Merged

Split skills registry and runtime modules#3481
senamakel merged 34 commits into
tinyhumansai:mainfrom
senamakel:feat/skill-registry-backends

Conversation

@senamakel

@senamakel senamakel commented Jun 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Split skills execution into skill_runtime and remote discovery/install into skill_registry.
  • Moved the skill executor agent under skill_runtime; kept discover/install/uninstall tooling under skill_registry.
  • Reused existing Node/Python runtime bootstrap modules for runtime availability checks before skill execution.
  • Added controller schemas and CLI/RPC surfaces for registry/runtime smoke scripts.
  • Wired the Skills frontend to the new RPC surfaces, including runtime preflight, install normalization, and uninstall.
  • Added docs plus focused Rust/Vitest/E2E coverage for registry/runtime behavior.

Problem

  • The existing skills surface mixed catalog discovery, install lifecycle, and execution concerns, making it harder to test and harder to operate from CLI/prod scripts.
  • Hermes-compatible remote catalog support needs a clean registry boundary and a runtime boundary that can reuse OpenHuman's Node/Python runtime modules instead of inventing another executor path.

Solution

  • Introduce src/openhuman/skill_registry as the catalog/cache/install/uninstall owner, backed by the Hermes skills.json API and local cache.
  • Introduce src/openhuman/skill_runtime as the run/cancel/log/recent-runs/runtime-resolution owner.
  • Move the skill executor agent into skill_runtime and leave the discovery/install agent in skill_registry.
  • Register both modules through the controller registry and expose schema RPCs for CLI/prod smoke automation.
  • Update React API clients and Skills components to call openhuman.skill_registry_* and openhuman.skill_runtime_* directly.
  • Warm the skills registry asynchronously on core startup; failures are logged and do not block core readiness.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage >= 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/pr-ci.yml. Run pnpm test:coverage and pnpm test:rust locally; PRs below 80% on changed lines will not merge. Local focused coverage was added; full diff coverage is left to CI for the full merged lcov gate.
  • Coverage matrix updated — N/A: this refactors and expands the existing skills surface rather than adding a new matrix feature row.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related: N/A: no new matrix feature ID.
  • No new external network dependencies introduced (mock backend used per Testing Strategy); the registry continues to use the Hermes catalog endpoint already scoped for this skills integration.
  • Manual smoke checklist updated if this touches release-cut surfaces: N/A: developer/operator smoke commands are documented in skill_registry and skill_runtime READMEs instead.
  • Linked issue closed via Closes #NNN in the ## Related section: N/A: no issue was provided for this branch.

Impact

  • Desktop/Tauri and standalone core both get the new domain split because wiring lives in Rust core controller registration.
  • Frontend skills flows now use registry/runtime RPCs instead of workflow execution RPCs for execution lifecycle.
  • Core startup now schedules a best-effort registry refresh in the background; OPENHUMAN_SKILL_REGISTRY_REFRESH_ON_BOOT=0 disables it.
  • Registry fetch timeout is 180s to accommodate the large Hermes catalog while keeping boot non-blocking.

Related

  • Closes: N/A
  • Follow-up PR(s)/TODOs: N/A

AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/skill-registry-backends
  • Commit SHA: d8c7c6b7c5d7531327553685a6b2d2262eddd817

Validation Run

  • pnpm --filter openhuman-app format:check via pre-push hook
  • pnpm typecheck
  • Focused tests: pnpm debug unit src/services/api/skillsApi.test.ts src/services/api/skillRegistryApi.test.ts src/components/skills/__tests__/WorkflowRunnerBody.test.tsx src/components/skills/__tests__/SkillsExplorerTab.test.tsx; cargo test --manifest-path Cargo.toml --test skill_registry_e2e -- --nocapture; cargo test --manifest-path Cargo.toml skill_registry --lib; cargo test --manifest-path Cargo.toml skill_runtime --lib
  • Rust fmt/check (if changed): cargo fmt --manifest-path Cargo.toml; cargo check --manifest-path app/src-tauri/Cargo.toml via pre-push hook
  • Tauri fmt/check (if changed): OPENHUMAN_DEV_PORT=1423 pnpm dev:app launched successfully; embedded core reached http://127.0.0.1:7788/rpc and scheduled the registry boot refresh.

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: skills registry/discovery/install and skills runtime/execution are now separate core modules with separate RPC/schema surfaces.
  • User-visible effect: Skills page installs, uninstalls, and runs skills through the new registry/runtime APIs; runtime availability is checked before Node/Python-backed runs.

Parity Contract

  • Legacy behavior preserved: installed skill metadata remains owned by workflows; existing run machinery was moved under skill_runtime and re-exported through new controllers.
  • Guard/fallback/dispatch parity checks: registry install still goes through hardened URL install logic; localhost HTTP install remains test-only behind OPENHUMAN_SKILL_INSTALL_ALLOW_LOCAL_HTTP=1; registry boot refresh is best-effort and does not block core load.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: this PR
  • Resolution (closed/superseded/updated): N/A

Summary by CodeRabbit

  • New Features

    • Interactive skill tiles with detail dialog, paginated catalog, multi-source/category filtering, debounced search, install/uninstall flows, and clearer empty/result states.
    • Runtime probe/resolution for Node/Python before runs with aggregated error reporting.
    • New built-in agents/tools to browse/search/install/uninstall skills and to probe runtimes.
  • Internationalization

    • Expanded Skills explorer/detail and runtime error translations across many languages.
  • Tests & Docs

    • Added unit, smoke, integration, and E2E tests plus registry/runtime READMEs.

senamakel added 13 commits June 7, 2026 02:33
Add two new RegistryKind variants (GithubSkillsDir, ClawHubApi) and
implement fetch logic for each:

- HermesHub: scans GitHub API contents/ endpoint for skill directories,
  constructs raw.githubusercontent.com download URLs for each SKILL.md
- ClawHub: paginates the clawhub.ai REST API (up to 5 pages), maps
  slug/displayName/summary/stats to CatalogEntry with clawhub:// scheme

Replace the non-existent awesome-openclaw and hermes-community default
sources with real endpoints. ClawHub install is gracefully rejected with
a message directing users to the OpenClaw CLI.
Two new built-in agents living in skill_registry/agent/:

- skill_setup: discovery and installation specialist — browses
  registries, searches by keyword, installs/uninstalls skills
- skill_executor: execution specialist — loads SKILL.md instructions,
  reads bundled resources, runs scripts via shell

Both follow the agent_memory pattern (external module, registered in
the central BUILTINS loader).
Rust E2E (`tests/skill_registry_e2e.rs`): exercises sources, browse,
search, install (happy path + duplicate rejection + ClawHub rejection)
against a real core router with isolated temp HOME. Registered in
`scripts/test-rust-e2e.sh` ALL_E2E_SUITES.

Playwright (`skills-registry.spec.ts`): adds RPC smoke tests for
sources, browse, and search endpoints via callCoreRpc.
Add both skill agents to the orchestrator's subagents allowlist and
add routing guidance in the decision tree so the orchestrator delegates
skill install/browse/run requests to the correct specialist.
…tion

New tools in skill_registry/tools.rs:
- skill_registry_browse: browse all enabled registries
- skill_registry_search: search catalog by keyword
- skill_registry_install: install from catalog entry
- skill_registry_sources: list configured sources

Orchestrator changes:
- Add all 4 tools to named tools list
- Set omit_skills_catalog = false so installed skills render in prompt
- Add render_installed_skills() to prompt.rs for session-start visibility
…Human label

- Add SkillDetailDialog popup when clicking any skill tile (catalog or
  installed) — shows description, version, author, stars, tags, and
  source URL with install button for uninstalled skills
- Fix duplicate badge chips: remove format badge from catalog tiles
  (source badge already identifies the registry), add friendly names
  to SourceBadge (Community, HermesHub, ClawHub)
- Replace "OpenHuman" fallback format label with actual format name
  or neutral "Skill" — we don't own third-party skills
- Change default format from "openhuman" to "agentskills" in index
  parser since skills follow the agentskills.io spec
- Add i18n keys for detail dialog across all 14 locales
…ggles

Backend:
- Add HermesJsonCatalog registry kind for the aggregated Hermes catalog
  at hermes-agent.nousresearch.com/docs/api/skills.json (90k entries
  with full metadata: descriptions, tags, author, version)
- Cap at 1000 entries per fetch to keep catalog manageable
- Change default format from "openhuman" to "agentskills"

Frontend:
- Add source toggle buttons (Community, HermesHub, ClawHub) that filter
  the catalog by registry source — users can enable/disable each
- Load sources on mount and default all enabled sources to active
Remove multi-source registry architecture (RegistrySource, RegistryKind,
per-backend fetch functions). The Hermes JSON catalog at
hermes-agent.nousresearch.com/docs/api/skills.json already aggregates
all sources (built-in, ClawHub, skills.sh, LobeHub, browse.sh).

- ops.rs: single fetch_catalog() from Hermes API with local caching
- store.rs: remove custom sources persistence
- schemas: remove add_source/remove_source RPCs, add categories RPC
- tools.rs: simplify to match new CatalogEntry (no source_id param)
- Frontend: sources are now string[], source toggles use entry.source
- E2E tests: updated to match simplified API shape
…client-side filtering

The 90k-entry Hermes catalog was being filtered client-side, causing
search to return nothing for queries like "docker". Now search goes
through the skill_registry_search RPC with 300ms debounce, results
are capped at 60 entries, and Playwright + unit tests cover the flow.
@senamakel senamakel requested a review from a team June 7, 2026 13:17
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Moves the skill registry to an aggregated Hermes catalog with caching, adds a skill_runtime execution/resolution surface, rewires RPCs/tools/agents, updates Skills Explorer to an RPC-driven source-aware catalog with a detail dialog, and adds unit, Playwright, and Rust E2E tests.

Changes

Skill Registry Refactor + Skill Runtime Module + Skills Explorer UI

Layer / File(s) Summary
Registry types & API client
app/src/services/api/skillRegistryApi.ts, app/src/services/api/skillRegistryApi.test.ts
Redefines CatalogEntry with Hermes fields (source, category, platforms, docs_path, commands, env_vars, license); removes RegistrySource; adds RegistryInstallResult, RegistryUninstallResult, ControllerSchemaSummary; changes search/sources/install/uninstall signatures and adds categories/schemas.
Registry backend: catalog ops & parsing
src/openhuman/skill_registry/ops.rs, src/openhuman/skill_registry/types.rs, src/openhuman/skill_registry/store.rs
Implements single aggregated catalog fetch/parse (Hermes format), boot-time refresh, cache-backed browse, search filtering by source/category, list_sources/list_categories, and deterministic download URL derivation; store simplified to cache-only.
Registry RPC, schemas & tools
src/openhuman/skill_registry/schemas/*, src/openhuman/skill_registry/tools.rs
Updates controller schemas/handlers/wire types to add categories, uninstall, schemas; SearchParams gains source/category; tools added for browse/search/install/sources/uninstall.
Skill runtime backend & schema surface
src/openhuman/skill_runtime/*, src/openhuman/skill_runtime/ops.rs, src/openhuman/skill_runtime/schemas.rs
Adds skill_runtime module: runtime requirement parsing, resolve_runtimes, per-runtime resolution (node/python), run-controller schemas/handlers (run/cancel/recent_runs/read_run_log/resolve_runtimes), and public run-machinery exports.
Runtime tool & integration
src/openhuman/skill_runtime/tools.rs, src/openhuman/tools/mod.rs
Adds SkillRuntimeResolveRuntimesTool and re-exports skill_registry/skill_runtime tools; integrates tools into the global tool list.
Agents & orchestrator integration
src/openhuman/skill_registry/agent/skill_setup/*, src/openhuman/skill_runtime/agent/skill_executor/*, src/openhuman/agent_registry/agents/loader.rs, src/openhuman/agent_registry/agents/orchestrator/*
Adds skill_setup and skill_executor agents (TOML, prompt, prompt builders), adds to BUILTINS, and enables orchestrator catalog/subagent/tool wiring.
Frontend: Skills Explorer UI & detail dialog
app/src/components/skills/SkillsExplorerTab.tsx, app/src/components/skills/UninstallSkillConfirmDialog.tsx
Refactors to RPC-driven catalog with catalogEntries/catalogTotal/catalogInitialized, sources and activeSources toggles, debounced search, accessible clickable tiles, merged SkillDetailDialog for catalog/installed metadata, and updated install/uninstall flows calling new APIs.
Workflow runner: runtime pre-resolution & API changes
app/src/components/skills/WorkflowRunnerBody.tsx, app/src/services/api/workflowsApi.ts, app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx
Adds inferRuntimeRequirement and ensureRuntimeAvailability using workflowsApi.resolveRuntimes before runs; migrates RPC names to skill_runtime_* and updates related types/tests.
Tests & E2E smoke
tests/skill_registry_e2e.rs, app/test/playwright/specs/skills-registry.spec.ts, app/src/components/skills/__tests__/*
Adds Rust E2E harness validating skill_registry RPCs with fixture catalog and on-disk install/uninstall; Playwright RPC smoke tests; updates unit tests and mocks for new catalog shapes and resolveRuntimes stubs.
Install safety & bootstrap
src/openhuman/workflows/ops_install.rs, src/core/jsonrpc.rs
Adds localhost-HTTP allowance for installs gated by env var; boots background catalog refresh on core startup.
i18n & docs
app/src/lib/i18n/*, src/openhuman/skill_registry/README.md, src/openhuman/skill_runtime/README.md
Adds/updates skills.explorer.showingOf and expanded skills.detail.* translations across locales; documents skill_registry and skill_runtime modules and environment overrides.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sanil-23

🐰 I hopped through Hermes with a catalog in tow,
Resolved runtimes so runs can go,
Agents and dialogs assembled just right,
Tests and installs danced deep into night,
A tiny hop, a developer's delight.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. working A PR that is being worked on by the team. labels Jun 7, 2026

@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: 6

🧹 Nitpick comments (5)
tests/skill_registry_e2e.rs (1)

481-483: 💤 Low value

Consider either asserting on stderr or removing the binding.

The _install_stderr binding is captured but not validated. If stderr presence is sufficient, the _ prefix is appropriate. However, if the intent is to later verify stderr content (e.g., confirm no unexpected warnings), consider adding an assertion or a comment clarifying the intent.

💡 Option A: Remove if not needed
-    let _install_stderr = install_result
-        .get("stderr")
-        .expect("install result must contain `stderr`");
+    // stderr is present but not validated; install success is confirmed by stdout and new_skills.
+    assert!(install_result.get("stderr").is_some(), "install result must contain `stderr`");
🤖 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 `@tests/skill_registry_e2e.rs` around lines 481 - 483, The binding
`_install_stderr` captures `install_result.get("stderr")` but isn't used; either
remove the binding if merely checking presence isn't needed, or replace it with
an assertion that validates stderr content (for example assert it is Some(...)
or matches expected/no warnings) to make the test intent explicit—update the
code referencing `_install_stderr` (the `install_result.get("stderr")` call)
accordingly and add a short comment if you intend to keep the presence-only
check.
src/openhuman/skill_registry/tools.rs (1)

236-288: 💤 Low value

Consider adding explicit is_concurrency_safe for consistency.

SkillRegistryUninstallTool (and SkillRegistryInstallTool) don't override is_concurrency_safe(), while the read-only tools explicitly return true. For write operations, the default (likely false) is correct, but adding explicit implementations would improve clarity and consistency.

🤖 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/skill_registry/tools.rs` around lines 236 - 288, The Tool impl
for SkillRegistryUninstallTool should explicitly implement is_concurrency_safe()
to return false (and do the same for SkillRegistryInstallTool) to make
write-operation concurrency semantics explicit; update the impl Tool for
SkillRegistryUninstallTool to add a fn is_concurrency_safe(&self) -> bool {
false } (and mirror this change in the SkillRegistryInstallTool impl) so the
concurrency intent is clear and consistent with the read-only tools that return
true.
app/src/components/skills/SkillsExplorerTab.tsx (2)

328-331: ⚡ Quick win

Consider adding Escape key to close the dialog.

Modal dialogs should close on Escape for keyboard accessibility. Add a useEffect with a keydown listener or handle it on the backdrop div.

⌨️ Example implementation
useEffect(() => {
  const handleEscape = (e: KeyboardEvent) => {
    if (e.key === 'Escape') onClose();
  };
  document.addEventListener('keydown', handleEscape);
  return () => document.removeEventListener('keydown', handleEscape);
}, [onClose]);
🤖 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/SkillsExplorerTab.tsx` around lines 328 - 331, Add
keyboard accessibility to SkillsExplorerTab by registering a keydown listener
that calls the existing onClose when the Escape key is pressed: inside the
SkillsExplorerTab component (ensure useEffect is imported), add a useEffect that
attaches a document.addEventListener('keydown', handler) where handler checks
e.key === 'Escape' and calls onClose(), and clean up the listener in the
effect's return; include onClose in the dependency array so the handler always
uses the latest callback.

893-899: 💤 Low value

Fallback English string defeats i18n.

The || \Showing...`` fallback will show English even for non-English locales if the key returns empty. If the translation key is properly defined, this fallback is unnecessary. If it's missing, you'd want to catch it during development.

Consider removing the fallback and ensuring the key exists in all locale files.

🤖 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/SkillsExplorerTab.tsx` around lines 893 - 899,
Remove the English fallback that defeats i18n: instead of using the expression
that appends `|| \`Showing ...\``, return only the localized string from
t('skills.explorer.showingOf') (use the existing variables catalogTotal and
displayedCatalog for replacements). Update the call in SkillsExplorerTab.tsx so
the component renders solely the translation (e.g., pass shown/total as
replacement params to t) and delete the fallback branch; ensure the translation
key exists in locales or add it during localization sync.
app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx (1)

166-167: ⚡ Quick win

Add explicit behavior tests for the new runtime-preflight path.

The new mocks keep tests green, but there’s still no assertion that resolveRuntimes is called with the inferred requirement, or that unavailable runtimes block runWorkflow and surface an error state. Adding those two cases will lock down the new branch.

As per coding guidelines, app tests should prefer behavior-oriented coverage.

🤖 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__/WorkflowRunnerBody.test.tsx` around lines
166 - 167, Add two behavior tests in WorkflowRunnerBody.test.tsx: (1) assert
that hoisted.resolveRuntimes is called with the inferred requirement when the
workflow is run (e.g., trigger the UI action that calls runWorkflow and verify
hoisted.resolveRuntimes was invoked with the expected requirement object), and
(2) simulate resolveRuntimes returning no matching runtimes (e.g.,
mockResolvedValue({ runtimes: [] })) and assert that runWorkflow is not called
and the component surfaces an error state/message. Use the existing
hoisted.resolveRuntimes and runWorkflow identifiers to locate and update tests.

Source: Coding guidelines

🤖 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`:
- Line 121: Update the onKeyDown handler in the SkillsExplorerTab component so
that when the Space key is detected (e.key === ' ' or 'Space'), you call
e.preventDefault() before invoking onClick(); keep the existing Enter behavior
intact. Locate the onKeyDown handler that currently checks e.key and the onClick
function reference and add the preventDefault call for the Space branch to stop
the page from scrolling.
- Line 218: The onKeyDown handler in SkillsExplorerTab (the inline handler that
checks e.key === 'Enter' || e.key === ' ') should call e.preventDefault() when
the Space key is used to activate the tile, matching SkillTile behavior; update
the handler so that when detecting the Space key it first calls
e.preventDefault() then invokes onClick(), ensuring default page scroll is
prevented on Space activation.
- Around line 397-399: Replace the hardcoded "License" label in
SkillsExplorerTab (the span rendering "License") to use the translator hook
useT() and the key t('skills.detail.license') (i.e., import/use useT() if not
already used in this component and call t('skills.detail.license') for the span
content); then add the new translation key "skills.detail.license" to en.ts and
mirror it in the other locale files with appropriate translations so all locales
cover this UI string.

In `@app/src/components/skills/WorkflowRunnerBody.tsx`:
- Around line 581-592: handleRun performs a runtime preflight using
inferRuntimeRequirement and workflowsApi.resolveRuntimes but handleRunJobNow
calls runWorkflow directly, allowing "Run now" to bypass the check; update
handleRunJobNow to perform the same preflight: call
inferRuntimeRequirement(selectedWorkflow), call workflowsApi.resolveRuntimes
with that requirement when present, throw the same error if any
resolved.runtimes are unavailable (mirroring the unavailable.map(...) join
logic), and only call runWorkflow after the preflight passes so both entry
points share the same runtime validation.
- Around line 586-590: The error message built from unavailable.map(...) in
WorkflowRunnerBody.tsx is hardcoded English; replace it with a localized string
via the component's useT() hook: add a translation key (e.g.
"runner.preflight.unavailable") that accepts placeholders for runtime name and
error, call const t = useT() (or reuse the existing t) in the component, and
build the thrown Error by mapping unavailable to
t('runner.preflight.unavailable', { runtime: runtime.runtime, error:
runtime.error ?? t('runner.preflight.unavailable_default') }) and joining those
localized pieces; ensure you add the new keys to the locale files so the UI uses
translations instead of raw English.

In `@src/openhuman/skill_runtime/tools.rs`:
- Around line 52-63: The execute method currently uses the stored Arc<Config>
(self.config) which can become stale; update execute to reload the config at the
start of the method (using the same pattern as the RPC handler, e.g., call
Config::load_or_init() or config_rpc::load_config_with_timeout()) and then pass
that fresh config into resolve_runtimes instead of self.config so runtime
paths/flags reflect current settings; keep all existing result handling
(serde_json::to_string, ToolResult::success) unchanged.

---

Nitpick comments:
In `@app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx`:
- Around line 166-167: Add two behavior tests in WorkflowRunnerBody.test.tsx:
(1) assert that hoisted.resolveRuntimes is called with the inferred requirement
when the workflow is run (e.g., trigger the UI action that calls runWorkflow and
verify hoisted.resolveRuntimes was invoked with the expected requirement
object), and (2) simulate resolveRuntimes returning no matching runtimes (e.g.,
mockResolvedValue({ runtimes: [] })) and assert that runWorkflow is not called
and the component surfaces an error state/message. Use the existing
hoisted.resolveRuntimes and runWorkflow identifiers to locate and update tests.

In `@app/src/components/skills/SkillsExplorerTab.tsx`:
- Around line 328-331: Add keyboard accessibility to SkillsExplorerTab by
registering a keydown listener that calls the existing onClose when the Escape
key is pressed: inside the SkillsExplorerTab component (ensure useEffect is
imported), add a useEffect that attaches a document.addEventListener('keydown',
handler) where handler checks e.key === 'Escape' and calls onClose(), and clean
up the listener in the effect's return; include onClose in the dependency array
so the handler always uses the latest callback.
- Around line 893-899: Remove the English fallback that defeats i18n: instead of
using the expression that appends `|| \`Showing ...\``, return only the
localized string from t('skills.explorer.showingOf') (use the existing variables
catalogTotal and displayedCatalog for replacements). Update the call in
SkillsExplorerTab.tsx so the component renders solely the translation (e.g.,
pass shown/total as replacement params to t) and delete the fallback branch;
ensure the translation key exists in locales or add it during localization sync.

In `@src/openhuman/skill_registry/tools.rs`:
- Around line 236-288: The Tool impl for SkillRegistryUninstallTool should
explicitly implement is_concurrency_safe() to return false (and do the same for
SkillRegistryInstallTool) to make write-operation concurrency semantics
explicit; update the impl Tool for SkillRegistryUninstallTool to add a fn
is_concurrency_safe(&self) -> bool { false } (and mirror this change in the
SkillRegistryInstallTool impl) so the concurrency intent is clear and consistent
with the read-only tools that return true.

In `@tests/skill_registry_e2e.rs`:
- Around line 481-483: The binding `_install_stderr` captures
`install_result.get("stderr")` but isn't used; either remove the binding if
merely checking presence isn't needed, or replace it with an assertion that
validates stderr content (for example assert it is Some(...) or matches
expected/no warnings) to make the test intent explicit—update the code
referencing `_install_stderr` (the `install_result.get("stderr")` call)
accordingly and add a short comment if you intend to keep the presence-only
check.
🪄 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: ab02581f-4f63-4c34-9e06-19403da54cee

📥 Commits

Reviewing files that changed from the base of the PR and between 418d3e6 and 3805116.

📒 Files selected for processing (70)
  • app/src/components/skills/SkillsExplorerTab.tsx
  • app/src/components/skills/UninstallSkillConfirmDialog.tsx
  • app/src/components/skills/WorkflowRunnerBody.tsx
  • app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx
  • app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx
  • app/src/components/skills/preflightGate.ts
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/services/api/__tests__/workflowsApi.test.ts
  • app/src/services/api/skillRegistryApi.test.ts
  • app/src/services/api/skillRegistryApi.ts
  • app/src/services/api/workflowsApi.test.ts
  • app/src/services/api/workflowsApi.ts
  • app/test/playwright/specs/skills-registry.spec.ts
  • scripts/test-rust-e2e.sh
  • src/core/all.rs
  • src/core/jsonrpc.rs
  • src/openhuman/agent/tools/run_workflow.rs
  • src/openhuman/agent_registry/agents/loader.rs
  • src/openhuman/agent_registry/agents/orchestrator/agent.toml
  • src/openhuman/agent_registry/agents/orchestrator/prompt.md
  • src/openhuman/agent_registry/agents/orchestrator/prompt.rs
  • src/openhuman/mod.rs
  • src/openhuman/skill_registry/README.md
  • src/openhuman/skill_registry/agent/mod.rs
  • src/openhuman/skill_registry/agent/skill_setup/agent.toml
  • src/openhuman/skill_registry/agent/skill_setup/mod.rs
  • src/openhuman/skill_registry/agent/skill_setup/prompt.md
  • src/openhuman/skill_registry/agent/skill_setup/prompt.rs
  • src/openhuman/skill_registry/mod.rs
  • src/openhuman/skill_registry/ops.rs
  • src/openhuman/skill_registry/schemas/controller_schemas.rs
  • src/openhuman/skill_registry/schemas/handlers.rs
  • src/openhuman/skill_registry/schemas/wire_types.rs
  • src/openhuman/skill_registry/store.rs
  • src/openhuman/skill_registry/tools.rs
  • src/openhuman/skill_registry/types.rs
  • src/openhuman/skill_runtime/README.md
  • src/openhuman/skill_runtime/agent/mod.rs
  • src/openhuman/skill_runtime/agent/skill_executor/agent.toml
  • src/openhuman/skill_runtime/agent/skill_executor/mod.rs
  • src/openhuman/skill_runtime/agent/skill_executor/prompt.md
  • src/openhuman/skill_runtime/agent/skill_executor/prompt.rs
  • src/openhuman/skill_runtime/mod.rs
  • src/openhuman/skill_runtime/ops.rs
  • src/openhuman/skill_runtime/run_machinery.rs
  • src/openhuman/skill_runtime/schemas.rs
  • src/openhuman/skill_runtime/tools.rs
  • src/openhuman/tools/mod.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/workflows/e2e_plumbing_tests.rs
  • src/openhuman/workflows/e2e_run_tests.rs
  • src/openhuman/workflows/ops_install.rs
  • src/openhuman/workflows/preflight.rs
  • src/openhuman/workflows/registry.rs
  • src/openhuman/workflows/schemas/handlers.rs
  • src/openhuman/workflows/schemas/mod.rs
  • tests/skill_registry_e2e.rs
💤 Files with no reviewable changes (1)
  • src/openhuman/workflows/schemas/mod.rs

Comment thread app/src/components/skills/SkillsExplorerTab.tsx Outdated
Comment thread app/src/components/skills/SkillsExplorerTab.tsx Outdated
Comment thread app/src/components/skills/SkillsExplorerTab.tsx
Comment thread app/src/components/skills/WorkflowRunnerBody.tsx Outdated
Comment thread app/src/components/skills/WorkflowRunnerBody.tsx Outdated
Comment thread src/openhuman/skill_runtime/tools.rs

@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/skill_runtime/tools.rs (2)

13-19: 💤 Low value

Consider removing the unused _config parameter if not required by the registration site.

The struct is a unit struct with no fields (line 13), and the constructor's _config parameter is never used (indicated by the _ prefix). If the registration/wiring code doesn't require this signature, consider simplifying:

-pub fn new(_config: Arc<Config>) -> Self {
+pub fn new() -> Self {
     Self
 }

If the parameter is required by a registration pattern elsewhere, consider adding a doc comment explaining why it's accepted but unused.

🤖 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/skill_runtime/tools.rs` around lines 13 - 19, The constructor
for SkillRuntimeResolveRuntimesTool currently accepts an unused parameter
_config: Arc<Config>; either remove that parameter from the new method signature
(change fn new() -> Self) and callers that register/construct
SkillRuntimeResolveRuntimesTool accordingly, or if the registration API requires
the signature, keep the parameter but add a doc comment to the
SkillRuntimeResolveRuntimesTool::new function explaining why Config is accepted
and intentionally unused; update references to the new constructor name
accordingly.

33-44: 💤 Low value

Consider documenting runtime aliases in the schema description.

The schema enum lists ["all", "node", "python"] but RuntimeRequirement::from_optional (context snippet 1) also accepts aliases like "nodejs", "javascript", and "python3". LLM agents relying on the schema won't discover these alternatives. Consider adding them to the enum or documenting in the description:

 "runtime": {
     "type": "string",
     "enum": ["all", "node", "python"],
-    "description": "Runtime to resolve. Defaults to all."
+    "description": "Runtime to resolve. Defaults to all. Accepts aliases: nodejs, javascript (node); python3 (python)."
 }
🤖 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/skill_runtime/tools.rs` around lines 33 - 44, The JSON schema
in parameters_schema() currently enumerates ["all","node","python"] but
RuntimeRequirement::from_optional accepts aliases like "nodejs", "javascript",
and "python3"; update parameters_schema() to either expand the "enum" to include
those aliases or explicitly list them in the "description" (e.g., mention
accepted aliases "nodejs", "javascript", "python3"), so LLM agents see all valid
runtime names—refer to parameters_schema() and RuntimeRequirement::from_optional
when making the change.
🤖 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/lib/i18n/fr.ts`:
- Line 285: Replace the mixed-language toast message for the key
'skills.explorer.uninstallSuccess' with a fully French, consistent label: change
its value from "Skill désinstallée avec succès." to "Compétence désinstallée
avec succès." so it matches the surrounding "Compétence" terminology used
elsewhere (update the string value for 'skills.explorer.uninstallSuccess').

---

Nitpick comments:
In `@src/openhuman/skill_runtime/tools.rs`:
- Around line 13-19: The constructor for SkillRuntimeResolveRuntimesTool
currently accepts an unused parameter _config: Arc<Config>; either remove that
parameter from the new method signature (change fn new() -> Self) and callers
that register/construct SkillRuntimeResolveRuntimesTool accordingly, or if the
registration API requires the signature, keep the parameter but add a doc
comment to the SkillRuntimeResolveRuntimesTool::new function explaining why
Config is accepted and intentionally unused; update references to the new
constructor name accordingly.
- Around line 33-44: The JSON schema in parameters_schema() currently enumerates
["all","node","python"] but RuntimeRequirement::from_optional accepts aliases
like "nodejs", "javascript", and "python3"; update parameters_schema() to either
expand the "enum" to include those aliases or explicitly list them in the
"description" (e.g., mention accepted aliases "nodejs", "javascript",
"python3"), so LLM agents see all valid runtime names—refer to
parameters_schema() and RuntimeRequirement::from_optional when making the
change.
🪄 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: 3f10b3e8-6317-472b-80f0-29a43b39511f

📥 Commits

Reviewing files that changed from the base of the PR and between 3805116 and 7e18e09.

📒 Files selected for processing (18)
  • app/src/components/skills/SkillsExplorerTab.tsx
  • app/src/components/skills/WorkflowRunnerBody.tsx
  • app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • src/openhuman/skill_runtime/tools.rs
✅ Files skipped from review due to trivial changes (4)
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ru.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/lib/i18n/en.ts
  • app/src/components/skills/tests/WorkflowRunnerBody.test.tsx
  • app/src/components/skills/SkillsExplorerTab.tsx

Comment thread app/src/lib/i18n/fr.ts Outdated
senamakel added 2 commits June 7, 2026 20:38
Replace mixed English/French "Skill désinstallée" with fully French
"Compétence désinstallée" to match surrounding locale wording.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 8, 2026
The job-level env sets CARGO_BUILD_JOBS=1 to serialize linker work and
avoid Bus errors on hosted runners, but the step-level env overrides it
back to 2. Remove the override so the linker runs single-threaded as
intended.

Closes tinyhumansai#3484
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 8, 2026
CARGO_BUILD_JOBS=1 alone is insufficient — the single
coverage-instrumented binary still exceeds the 7GB RAM on
ubuntu-22.04 runners. Adding swap prevents OOM kills during
the link phase.

Ref: tinyhumansai#3484
senamakel added 15 commits June 7, 2026 21:59
The PR CI workflow (pr-ci.yml) is the one that actually runs on PRs,
not coverage.yml (workflow_dispatch only). Add 8GB swap and reduce
CARGO_BUILD_JOBS to 1 to prevent rust-lld OOM during coverage linking.

Ref: tinyhumansai#3484
CI containers don't have sudo installed — the process already runs as
root. Add `options: --privileged` so the swapon syscall is permitted,
and remove sudo prefix from all swap commands.

Ref: tinyhumansai#3484
The CI container lacks sudo, so swap setup fails inside it. Switch
the Rust Core Coverage job to run on the bare ubuntu-22.04 host
(installing system deps via apt) so the pierotofy/set-swap-space
action works. With 10GB swap (~17GB total), CARGO_BUILD_JOBS=2
is safe again.

Ref: tinyhumansai#3484
Running on a bare host (for swap support) instead of the CI container
means a cold Rust cache and apt-get overhead. The previous 30-min
timeout cancelled the job at ~34 min. Bumping to 45 min gives enough
headroom for cold-cache runs.

Ref: tinyhumansai#3484
The bare-host approach worked (no OOM) but was too slow without the
container's pre-built toolchain and deps (34 min, hit 30-min timeout).

Restore the CI container image for its build optimizations. Use
`--privileged` to allow `swapon` inside the container (runs as root,
no sudo needed). With 10GB swap + container perf, CARGO_BUILD_JOBS=2
should complete well within 30 min.

Ref: tinyhumansai#3484
The --privileged container shares the host's /swapfile mount. mkswap
refuses to format a mounted swapfile. Add swapoff -a before fallocate
to cleanly resize the existing host swap from ~4GB to 10GB.

Ref: tinyhumansai#3484
The host's /swapfile is already mounted and can't be reformatted from
inside the container even with --privileged. Create additional swap at
/tmp/extra-swap instead, stacking on top of the existing ~4GB host
swap for ~14GB total virtual memory.

Ref: tinyhumansai#3484
swapon rejects swap files on overlay/tmpfs filesystems (Invalid
argument). The /__w directory is bind-mounted from the host's real
ext4 filesystem, so swapon works there.

Ref: tinyhumansai#3484
…rror

The 10GB swap file on /__w consumed too much disk, leaving insufficient
space for coverage-instrumented build artifacts. rust-lld crashed with
SIGBUS (bus error) when the disk filled up. Reducing swap to 4GB frees
~6GB for the build, and CARGO_BUILD_JOBS=1 ensures only one linker runs
at a time to keep peak memory within the 7GB RAM + 4GB swap budget.
The bus error (SIGBUS) during coverage linking was caused by disk
exhaustion, not memory exhaustion — the swap file consumed disk that
the linker needed for its enormous coverage-instrumented binaries.
Remove swap entirely and instead free ~15GB by removing pre-installed
Android SDK, .NET, and hostedtoolcache from the runner. Keep
CARGO_BUILD_JOBS=1 to limit peak memory within the 7GB RAM budget.
The previous rm -rf /opt/hostedtoolcache ran inside the container where
that path doesn't exist. The host's toolcache is bind-mounted at /__t
inside the container — deleting /__t/* actually frees ~8GB on the shared
partition, preventing the SIGBUS during coverage-instrumented linking.
The catalog_mirrors_builtins test enforces that every built-in agent
has a matching entry in RESOURCE_CATALOG. The new skill_setup and
skill_executor agents were missing.
Cover previously-untested branches in four files targeted by the
diff-cover gate on PR tinyhumansai#3481:

skillRegistryApi.ts (was 63%):
- search() with query-only, with source+category, and envelope shape
- sources() / categories() happy path and envelope unwrapping
- install() with missing new_skills → [] fallback
- browse() with forceRefresh=true and default false

workflowsApi.ts (was 93.8%):
- resolveRuntimes('all') and resolveRuntimes() (default) using empty
  params object, covering the ternary on line 538

SkillsExplorerTab.tsx (was 42.8%):
- SkillFormatBadge fallback label for unknown formats
- SkillTile / CatalogTile keyboard activation (Enter key)
- SkillDetailDialog open/close with all metadata fields
- Detail dialog footer install button triggering handleRegistryInstall
- Registry install success toast and error toast paths
- Source toggle button rendering and single-source filter triggering search
- Catalog count and installed count badges
- Legacy scope badge display
- Empty registry state and empty installed state
- Retry button for both catalog and installed error states
- Refresh button sending forceRefresh=true
- Hermes-first sort order in installed view

WorkflowRunnerBody.tsx (was 69.1%):
- skillsError display when listWorkflows rejects
- Recent runs DONE / DEGENERATE / FAILED status badge colors
- Run row expand → tailing indicator + log content render
- readRunLog error path surfaced in viewer
- Expand/collapse toggle icons (▾ / ▸)
- All-runs vs skill-scoped recent runs heading
- renderField for boolean (checkbox), integer (number), and string
- handleRemoveJob calling openhumanCronRemove + list refresh
- loadJobHistory error path (graceful, no crash)
- History run row with null output → historyNoOutput placeholder
- handleSaveSchedule with missing required fields → scheduleError
- buildCronJobName with non-empty inputs (via save schedule flow)
- ensureRuntimeAvailability failure when python runtime unavailable
- Preflight gate pill in run error display
@senamakel senamakel merged commit c9f9428 into tinyhumansai:main Jun 8, 2026
17 of 19 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/. feature Net-new user-facing capability or product behavior. 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.

1 participant