Skip to content

refactor(workflows): merge skills into the unified workflows primitive#3324

Merged
senamakel merged 87 commits into
tinyhumansai:mainfrom
sanil-23:refactor/skills-workflows-merge
Jun 5, 2026
Merged

refactor(workflows): merge skills into the unified workflows primitive#3324
senamakel merged 87 commits into
tinyhumansai:mainfrom
sanil-23:refactor/skills-workflows-merge

Conversation

@sanil-23
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 commented Jun 4, 2026

Summary

  • Unifies the legacy skills domain into a single workflows primitive: src/openhuman/skills/src/openhuman/workflows/, with the RPC surface, agent tools, type idents and FE renamed in lockstep (skills_*workflows_*, run_skillrun_workflow, Skill*Workflow*).
  • Dissolves the dead agent_workflows domain + phase machinery (workflow_load / workflow_phase tools removed); the unified create form combines skill + workflow (adds when_to_use).
  • Removes the bundled default skills and the retired Runners/scheduled-jobs FE; workflows now live on the Intelligence surface only.
  • Adds the run_workflow execution + discovery tool family (list_workflows, describe_workflow, list_workflow_runs, read_workflow_run_log, run_workflow, await_workflow) wired into the orchestrator.
  • e2e coverage for the workflow create→run path, the task→workflow spine, and run-spawn guards.

Demo

🎥 Loom walkthrough

Problem

The product carried two overlapping primitives — "skills" and "agent workflows" — with parallel RPC namespaces, FE surfaces and agent tools. This split confused the create/run UX and left dead machinery (the QuickJS-era runner, phase tools, scheduled-jobs overview) on the tree.

Solution

Collapse everything onto one workflows domain. The rename is mechanical-but-wide (Rust core + RPC wire keys + FE callers + i18n), staged as discrete commits (domain dir → RPC methods → type idents → execution path → FE → cleanup) so each step is reviewable. The run-path loader is unified with discovery so created workflows are immediately runnable.

Submission Checklist

  • Tests added or updated — workflow create→run e2e, task-spine e2e, run_workflow spawn-guard tests
  • N/A: Diff coverage ≥ 80% — the diff-cover Coverage Gate is skipping on this PR; consolidation/rename of existing covered code.
  • N/A: Coverage matrix updated — no new feature rows (rename/consolidation of existing skills features).
  • All affected feature IDs listed under ## Related — N/A (no new matrix IDs)
  • No new external network dependencies introduced
  • N/A: Manual smoke checklist — no new release-cut surface (existing Intelligence/workflows surface).
  • N/A: Linked issue closed via Closes #NNN — no tracking issue for this refactor.

Impact

  • Desktop (core + Tauri + React). No mobile/CLI-specific behavior change beyond the shared RPC rename.
  • Migration/compat: JSON-RPC methods and wire keys rename skills_*workflows_* and skill_idworkflow_id; FE updated in lockstep. External callers of the old skills_* RPC names would need updating.
  • Removes dead code paths (agent_workflows, phase tools, bundled default skills, Runners tab).

Related

  • Closes:
  • Follow-up PR(s)/TODOs: task-sources board work is intentionally not in this PR (kept on a separate branch); it stacks on top of this migration.

AI Authored PR Metadata

Linear Issue

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

Commit & Branch

  • Branch: refactor/skills-workflows-merge
  • Commit SHA: d000b5cd

Validation Run

  • pnpm --filter openhuman-app format:check — prettier --check clean
  • pnpm typecheck — tsc --noEmit clean
  • Focused tests: cargo check --tests + cargo check --bin openhuman-core green against current upstream/main; i18n parity clean
  • Rust fmt/check (if changed): cargo fmt --check clean; cargo check green
  • N/A: Tauri fmt/check — no app/src-tauri changes in this PR.

Validation Blocked

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

Behavior Changes

  • Intended behavior change: one unified "workflows" primitive replacing skills + agent_workflows.
  • User-visible effect: skills surface/tabs consolidated onto Intelligence → Workflows; create form unified.

Parity Contract

  • Legacy behavior preserved: workflow create/run/discover parity with prior skill run path; run-path loader unified with discovery.
  • Guard/fallback/dispatch parity checks: run_workflow spawn guards (re-entrancy, await cap, backstop) covered by tests.

Duplicate / Superseded PR Handling

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

Summary by CodeRabbit

  • New Features

    • Intelligence gains a Workflows tab with deep-linking and new Create (/workflows/new) and Run (/workflows/run) routes.
    • Create workflow supports "When to use" and edit mode; installed workflows list offers Run and Uninstall actions.
  • Improvements

    • Runner: locked-run mode, stop in-flight runs, auto-refreshes recent runs, and shows scheduled-job inputs.
  • Refactor

    • UI copy and navigation shifted from "skills" to "workflows" across runner, settings, and scheduling.
  • Tests

    • Added unit and end-to-end coverage for workflows, runner, scheduling, and orchestration.

sanil-23 and others added 30 commits June 4, 2026 02:20
…-issue-crusher, pr-review-shepherd)

First phase of the workflows-unify refactor. Drops the 3 bundled default
skills and the dev-workflow default-seeding test. seed_default_skills is now
a no-op (DEFAULT_SKILLS empty) until we ship example workflows under the
unified primitive. Remaining crusher/shepherd references are prose comments
(cleaned in later phases) or tmp-dir tests that don't depend on the bundle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Removes the agent_workflows domain (WORKFLOW.md / phase playbooks) and the
phase tooling: workflow_load / workflow_phase / the AgentWorkflow* tools, the
phase-keyed lifecycle (on_pick_up_task / on_close_task / on_enter_directory),
per-phase scripts/tool-scope/working-dir context, the todos auto-fire stub,
the PromptContext.workflows field + session plumbing, the RPC controllers, and
the about_app capability entry.

Rationale: pickup/close are the dispatcher/orchestrator's job (phases
duplicated it); phases only fire on the task-board path so they'd break
manual/scheduled runs; and the whole subsystem was unwired (auto-fire stub,
no defaults, discovery tool unwired). Guidance is not a primitive — it lives
in a workflow's own body / system prompt / memory.

Core bin + lib unit tests compile green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aths

git mv src/openhuman/skills -> src/openhuman/workflows and rewrite all
openhuman::skills paths -> openhuman::workflows. Internal Skill* type names
and skills_* RPC methods unchanged in this commit (follow-ups). Runtime
workspace data path (<workspace>/skills/) intentionally left as-is — renaming
it would break existing user workspaces. Green: cargo check --bin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kill->run_workflow

Controller methods openhuman.skills_{list,run,describe,recent_runs,read_run_log,
read_resource,create,install_from_url,uninstall} -> openhuman.workflows_*;
all_skills_*_controllers/schemas -> all_workflows_*; handle_skills_* ->
handle_workflows_*; the run tool run_skill -> run_workflow (RunWorkflowTool,
RUN_WORKFLOW_TOOL_NAME, file renamed). Runtime workspace data path left as
<workspace>/skills/. Frontend RPC callers updated in Phase 5. Green: cargo check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Phase 2 core)

Renames every skills-domain type identifier (Skill, SkillDefinition, SkillInput,
Skill*Tool, Skill* events, SkillScope/Frontmatter/Registry/Summary/Match, the
Skills*Params/Result RPC wire types, SkillsSection, …) to Workflow*. Three
overloaded idents are deliberately PRESERVED because they belong to the
integration-delegation surface, not the skills domain:
  - SkillDelegationTool  (the delegate_to_integrations_agent tool)
  - SkillsWildcard       ({ skills = "*" } integration-toolkit wildcard)
  - ToolScope::Skills     (integration-toolkit delegation variant)
Lowercase data paths (<workspace>/skills/) and field names left intact.
Test fixtures updated. cargo check --bin + cargo test --lib green; all 169
workflows-domain tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make workflows executable + composable (the unified primitive's core
behavior), and align the orchestrator's workflow surface to the new
vocabulary.

3a — execution (function-call semantics):
- run_log: add `RunOutcome` + `read_terminal_outcome` to parse a run's
  terminal footer (status + final output) from its log.
- schemas: add `await_run_outcome` — runtime polls the log until terminal
  or a wait budget elapses (the LLM issues one tool call, never busy-waits).
- run_workflow tool: spawn a workflow as a subagent, await its result
  inline up to `wait_seconds` (default 90, max 600), then auto-detach to a
  `run_id`. `wait_seconds: 0` = fire-and-forget chaining. New
  `await_workflow(run_id)` re-attaches to a detached run.

3b — discovery: rename the 8 LLM-facing introspection tools to the
workflow vocabulary (`list_workflows`, `describe_workflow`,
`read_workflow_resource`, `list_workflow_runs`, `read_workflow_run_log`,
`create_workflow`, `install_workflow_from_url`, `uninstall_workflow`);
`workflow_id` param with `skill_id` accepted as a back-compat alias. Wire
the orchestrator allow-list: drop deleted `workflow_load`/`workflow_phase`,
swap `run_skill` → `run_workflow` + `await_workflow` + the discovery set.

3c — guardrails (coarse process-global, not per-lineage): spawn backstop,
concurrent-await/nesting cap, and a (workflow_id+inputs) re-entrancy lock
so a confused LLM can't tip a legitimate A→B→A chain into a tight loop —
without blocking legitimate repeated runs.

Also: fix stale Phase-2 leftovers — drop deleted `agent_workflow_*` tools
from the productivity test lists; pin `ToolCategory::Workflow` wire format
to "skill" (it's the integration-tool category that `category_filter`
depends on, not a WORKFLOW.md bundle — revisited in Phase 4); update the
legacy run_skill→builtin rewrite shim to also match `run_workflow`.

Tools suite green (754 passed, single-threaded); workflows domain 171.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The integrations_agent drives a single Composio toolkit per spawn. Now
that workflows are a first-class primitive the orchestrator discovers and
runs (`list_workflows` / `run_workflow` / `await_workflow`, wired in Phase
3), the integrations_agent should carry no workflow surface:

- agent.toml: drop the deleted `workflow_load` / `workflow_phase` from its
  `named` allow-list — tool surface is now composio-only
  (`composio_list_tools`, `composio_execute`, `file_read`).
- prompt.rs: remove the bespoke `## Available Skills` workflow catalogue
  (and its `render_available_skills` / `xml_escape` helpers). It advertised
  workflows the agent has no `run_workflow` tool to invoke — a promise it
  couldn't keep. Tests updated; `ctx_with` drops the now-unused skills arg.

Orchestrator-side wiring landed in Phase 3's agent.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2 renamed the core JSON-RPC controllers skills_*→workflows_*, which
broke every frontend caller still invoking `openhuman.skills_*`. Repoint
all 9 RPC method strings in skillsApi.ts (list, describe, run, create,
install_from_url, uninstall, read_resource, read_run_log, recent_runs) to
`openhuman.workflows_*`, and update the unit-test expectations + e2e/
playwright RPC mocks to match. Doc comments in the skills components that
referenced the old method names are updated for accuracy.

This is the functional FE fix. The cosmetic UI consolidation (Skills→
Workflows page/component renames, Runners→Intelligence, cutting the
freshness/centrality/cohesion memory tabs) is still pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…emoved pieces

Factual corrections tied to the refactor (not taxonomy relabeling — kept
deliberately narrow):

- `run_skill` → `run_workflow` across all locales that embed the tool name
  in the skills-runner schedule help text (the tool was renamed in Phase 3).
  The token is a locale-invariant code identifier.
- en `skillsRunner.panelDesc`: drop the "Use Dev Workflow instead" advice
  (Dev Workflow was removed in Phase 1) and point at the panel's own
  Schedule section; "skill" → "workflow" in that one sentence.

Verified: app tsc --noEmit clean; 146 skills component + skillsApi tests pass.

Deliberately NOT done here (founder/translator decisions, flagged in the
proposal): the user-facing Skills→Workflows IA rename, route/URL renames,
Runners→Intelligence restructure, the orphaned DevWorkflowPanel + its
~60 `settings.devWorkflow.*` keys, and translating new copy into the 12
non-English locales.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… detail, inputs optional+described

Per product direction:

- Remove the "Runners" sub-tab from the Connections (/skills) page. Its
  scheduled-jobs overview (SkillsDashboard) is dropped from the tab chrome;
  the run+catalog surface stays at /skills/run. (SkillsDashboard.tsx is left
  in place, just unmounted — reversible.)
- Run a workflow from its detail: SkillDetailDrawer gains a "Run workflow"
  CTA that closes the drawer and opens /skills/run?skill=<id> (the existing
  runner, pre-selected). This is the run entry point now that the Runners
  tab is gone. SkillsRun's back-target moves /skills?tab=runners → /skills.
- CreateSkillForm inputs: additional inputs now default to required:false
  (optional unless the author ticks Required) and every added input REQUIRES
  a description (validated; inline error shown; blocks submit). Help text +
  new i18n key updated (en).

Verified: app tsc --noEmit clean; 465 skills + pages tests pass
(CreateSkillForm/SkillDetailDrawer/SkillsRun/SkillsRunnerBody included).

Note: new i18n values added to en.ts only; the 12 other locales fall back
to English until the Phase 6 translation pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d out)

The standalone scheduled-jobs dashboard (SkillsDashboard) was the body of
the removed Runners tab. It is being phased out, so delete it rather than
leave it dormant:

- Remove src/pages/SkillsDashboard.tsx + SkillsDashboard.test.tsx. It was
  orphaned after Phase 5c (only its own test still imported it).
- Per-workflow scheduling stays put: ScheduledCronCard + the "Save
  schedule" / saved-jobs list inside SkillsRunnerBody (/skills/run) are
  untouched — only the global overview page is gone.
- Refresh stale comments in SkillsRunnerBody + ScheduledCronCard that
  pointed at the deleted file.

Verified: app tsc --noEmit clean; 457 skills + pages tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te dead agent_workflows FE

Workflows are an intelligence concern, not a connection. Move the unified
workflow surface to the Intelligence → Workflows tab and remove it from the
Connections page.

- New components/intelligence/WorkflowsTab.tsx: the skillsApi-backed workflow
  surface (list + detail drawer with "Run workflow" CTA + create / install /
  uninstall). Intelligence's "workflows" tab now renders this.
- Connections (Skills.tsx): drop the discovered-workflow cards + their
  create/install/uninstall modals, drawer, fetch, and toast plumbing.
  Connections is integrations-only (Composio / channels / MCP) now.
- Delete the dead old `agent_workflows` FE: its Rust backend was dissolved in
  Phase 2 and its `workflowsApi` calls (`workflows_read`/`workflows_phase`)
  are gone while `workflows_list/create/uninstall` now collide with the
  SKILL.md controllers. Removed AgentWorkflows page, workflowsApi,
  workflowsSlice (+ store registration), and components/workflows/* (Card,
  DetailDrawer, CreateModal, PhaseEditor) + their tests.
- /workflows route now redirects to /intelligence?tab=workflows; Intelligence
  honours `?tab=` for deep-linking. en i18n for `workflows.*` retitled from
  the dissolved "Agent Workflows" concept to the unified primitive +
  installFromUrl key.
- Tests: deleted the moved Skills.discovered-skills test; added WorkflowsTab
  coverage.

Verified: app tsc --noEmit clean; 1341 tests pass across pages, intelligence,
skills, store, services.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…flow (add when_to_use)

The Workflows-tab create form is now a combo of the two legacy forms: the
runners "new skill" form (procedure + declared inputs) and the old agent-
workflow form (the `when_to_use` trigger — the "reason to run" a bare
procedure md lacks).

Core:
- CreateSkillParams / WorkflowsCreateParams gain an optional `when_to_use`.
  create_skill writes it to the workflow's skill.toml; falls back to the
  description when omitted. A workflow with a distinct trigger but no inputs
  now still gets a skill.toml so the trigger persists. render_skill_toml's
  param renamed description→when_to_use to match what it actually writes.

Frontend:
- CreateSkillInput gains `whenToUse`; createSkill sends it as `when_to_use`.
- CreateSkillForm adds an optional "When to use" field between Description
  and Inputs. (Inputs already: optional-by-default, description-required.)

Tests: new Rust test (distinct when_to_use → skill.toml without inputs);
new FE test (whenToUse flows into the payload). Verified: 172 workflows
tests pass; app tsc clean; CreateSkillForm 16 + skillsApi + WorkflowsTab green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ied primitive

The tab subtitle still carried the dissolved agent-workflows copy
("Lifecycle-bound rule sets…"). Reword to describe the unified runnable
workflow (goal + steps; create / install / run), consistent with the
WorkflowsTab header copy. en only; locales fall back until the i18n pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ot divergence)

Adds src/openhuman/workflows/e2e_plumbing_tests.rs — hermetic, no-network
plumbing confirmation for the unified workflow:

- B (passing): a mock-LLM (scripted ScriptedProvider) drives the real agent
  tool-loop to call `list_workflows` (sees a seeded project workflow) and
  `run_workflow` (unknown id → clean tool error). Confirms both tools are
  wired into the orchestrator's tool surface and execute end-to-end.
- C (passing): `await_run_outcome` returns a run's terminal DONE footer, and
  auto-detaches (None) when the run outlives the wait budget — the
  run_workflow await/detach plumbing.
- A (ignored): create → registry round-trip. Documents a REAL gap the test
  surfaced: the create/discover path writes to `.openhuman/skills` (+ home),
  but the RUN path (`get_skill` → `load_skills`) scans only `<ws>/skills`.
  So a workflow created on the Intelligence tab is discoverable but NOT
  runnable ("unknown workflow"). Ignored with a reason; asserts the target
  post-fix behaviour. Un-ignore once load_skills is unified with
  discover_skills.

The autonomous run that run_workflow spawns builds its provider from config
(no test-injection seam), so these tests confirm everything up to that
boundary; the inner run is covered by the subagent_runner suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… test seam)

Confirms the genuinely-new execution plumbing with a scripted LLM, no network:

- factory.rs: add a `#[cfg(test)]` process-global provider override
  (`test_provider_override`) consulted at the top of `create_chat_provider`.
  Inert in release (the check is cfg(test)). Needed because the inner workflow
  run is a detached `tokio::spawn` that rebuilds its provider from config — a
  thread/task-local seam can't reach it.

- e2e_run_tests.rs (2 tests, #[ignore]+serial — they touch process-global
  state: the provider override + OPENHUMAN_WORKSPACE; run with
  `--ignored --test-threads=1`):
  1. inner workflow run: `spawn_skill_run_background` builds the autonomous
     agent, the mock LLM finishes the turn, the run reaches a DONE footer, and
     `await_run_outcome` returns it with the agent's output.
  2. composition: the orchestrator tool-loop calls `run_workflow` → spawns the
     inner run (same mock) → awaits → the inner DONE result flows back into the
     conversation; orchestrator then wraps up.

One mock LLM serves both the orchestrator and the inner run, routing by prompt
content. Seeds a runnable workflow at the resolved `<ws>/skills/<id>`.

Default suite unaffected (factory 90, workflows 174 green; the e2e_run tests
are ignored by default).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kflow → Done

Adds test 3 to the mocked-LLM run suite: the complete chain the unified
workflow is meant to support, with a scripted LLM and no network.

  create a Todo card on the board → mark Ready → dispatch_card claims it
  (→ InProgress) and detaches the autonomous run → the orchestrator (mock)
  calls run_workflow → the inner workflow agent (mock) runs to DONE →
  await_run_outcome returns it → the orchestrator wraps up → write_back marks
  the card Done with the run output as evidence.

Asserts: dispatch returns Running, the card reaches Done, and the run output
("ORCHESTRATOR_DONE") is captured as the card's evidence.

#[ignore]+serial like the others (process-global provider override +
OPENHUMAN_WORKSPACE). Run: cargo test --lib workflows::e2e_run_tests --
--ignored --test-threads=1  → 3 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…"runners") skill→workflow

Scoped to exactly what PR tinyhumansai#2882 ("scheduled dashboard + run/new pages …", the
runners / new-skill feature that merged into workflows) added — the run-path
loader in registry.rs:

  load_skills          → load_workflows
  get_skill            → get_workflow
  seed_default_skills  → seed_default_workflows
  DEFAULT_SKILLS       → DEFAULT_WORKFLOWS

Plus their callers (describe_workflow tool, workflows_run RPC,
spawn_skill_run_background, task_dispatcher executor resolution, jsonrpc seed).

Deliberately NOT touched (verified pre-tinyhumansai#2882 / not the runners):
- discover_skills / discover_skills_inner (the original SKILL.md discovery,
  tinyhumansai#723) and ops_discover::load_skills — the openhuman-skills catalog path
- create_skill / read_skill_resource (tinyhumansai#740)
- on-disk SKILL.md / skill.toml / skills/ (the tinyhumansai/openhuman-skills
  registry contract) and all catalog/install interop
- the composio "skill" tool category and built-in capability skills

Internal-symbol rename only; no behavior change, no on-disk change.
Verified: bin compiles; workflows 174 pass; the 3 mocked-LLM lifecycle e2e
tests still pass (--ignored --test-threads=1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…th skill→workflow

Continues the runner (tinyhumansai#2882) rename — the backend execution path it added,
verified introduced by db3fdc2:

  spawn_skill_run_background   → spawn_workflow_run_background
  SKILL_RUN_MAX_ITERATIONS     → WORKFLOW_RUN_MAX_ITERATIONS
  run-log banner "==== skill_run:" + "skill_run" log labels → "workflow_run"
  (writer write_header + reader scan_runs + tests changed together — the
   app's own internal run-log format)

Deliberately NOT renamed — `skill_id` stays, because it's a live FE wire key,
not an internal label:
  - WorkflowsRunParams.skill_id (RPC param the frontend sends)
  - ScannedRun.skill_id (serialized field the frontend reads)
  - skillsApi sends/reads `skill_id`
Renaming it is a coordinated Rust+FE RPC change for a separate pass.

On-disk SKILL.md/skill.toml + openhuman-skills interop untouched (the run-log
dir <ws>/skills/.runs and the skill.toml filename are unchanged).

Verified: bin compiles; workflows 174 pass; 3 mocked-LLM lifecycle e2e tests
pass (--ignored --test-threads=1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Rust + FE lockstep)

Completes the runner rename — the last skill-named piece was the JSON-RPC
wire key shared by the workflows_run / describe / read_resource / recent_runs
methods. Renamed in lockstep so the contract stays consistent:

Rust (schemas.rs, run_log.rs):
- WorkflowsRun/Describe/ReadResource/RecentRunsParams `skill_id` → `workflow_id`
- the controller-schema param declarations `name: "skill_id"` → `"workflow_id"`
- WorkflowsReadResourceResult / WorkflowRunStarted / ScannedRun `skill_id` → `workflow_id`
- run_workflow.rs reads `started.workflow_id` (kept the tool's `skill_id` arg
  back-compat alias)
- tools.rs read_resource response key → `workflow_id` (kept its read_workflow_id
  / recent-runs `skill_id` aliases)

Frontend (skillsApi.ts + test, SkillsRunnerBody):
- every `workflows_*` RPC now sends/reads `workflow_id` (snake_case wire key);
  the camelCase `skillId` JS variables are left as-is (internal FE naming)

Left untouched: ops_discover `read_skill_resource` (openhuman-skills discovery),
on-disk SKILL.md/skill.toml, and the run_workflow tool's legacy skill_id alias.

Verified: bin compiles; workflows 174 + 3 e2e pass; app tsc clean; 494 FE
skills/intelligence/api tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rkflows are now runnable

The create/list path wrote to `~/.openhuman/skills` + `<ws>/.openhuman/skills`,
but the RUN path (`load_workflows` → `get_workflow`) scanned only `<ws>/skills`.
So a workflow authored on the Intelligence tab was discoverable but NOT runnable
— `run_workflow` → `get_workflow` returned "unknown workflow".

Fix: `load_workflows` now enumerates via `discover_skills` (the same roots the
create/list path uses — user, project [trusted], legacy; deduped by scope
precedence) and loads each one's runnable `WorkflowDefinition`:
- `skill.toml` present → parse it (id / when_to_use / [[inputs]] / [github]) +
  the `SKILL.md` body as the inline system prompt;
- `SKILL.md` only → synthesize a minimal definition (id = slug, when_to_use =
  frontmatter description) so a bare workflow is still runnable.

Create → discover → run is now a single path. Un-ignored the gap test
(`create_then_registry_roundtrip_preserves_when_to_use_and_inputs`) — it now
passes by default and guards the regression.

Verified: bin compiles; workflows 175 pass (test A live), agent harness 407,
3 mocked-LLM lifecycle e2e pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd resolves Done

Covers the "no workflow fits" path: the orchestrator must complete the task
itself rather than calling run_workflow.

- MockLlm.workflow_id is now Option<String>: Some(id) → call run_workflow
  (existing tests), None → answer the task directly.
- New test 4 (task_with_no_workflow_runs_directly_and_resolves_done):
  dispatch a card with no seeded workflow + a mock that picks none → the
  orchestrator answers directly ("TASK_DONE_NO_WORKFLOW") → write_back marks
  the card Done. Asserts: card Done, evidence is the direct answer, and
  scan_runs is empty (no inner workflow run was ever spawned).

e2e_run suite: 4 passed (--ignored --test-threads=1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ace skill→workflow

Scoped strictly to the files the runner PR (tinyhumansai#2882) introduced, per its
git --name-status. The openhuman-skills discovery surface (PR tinyhumansai#740/tinyhumansai#833:
SkillCard, SkillDetailDrawer, InstallSkillDialog, SkillResourcePreview/Tree,
UninstallSkillConfirmDialog, CreateSkillModal, skillsApi) keeps its skill
vocabulary — the Workflows tab simply reuses it.

Renamed (runner-origin only):
- SkillsRunnerBody → WorkflowRunnerBody
- SkillsRunnerPanel → WorkflowRunnerPanel
- CreateSkillForm → CreateWorkflowForm  (the runner's form; CreateSkillModal
  that wraps it stays skill-named and just updates its import)
- pages SkillsRun → WorkflowsRun, SkillNew → WorkflowNew

Routes, the ?skill= query param, skillsApi, and all openhuman-skills i18n keys
are left untouched. typecheck clean; 50 affected specs green.

Co-Authored-By: Claude <noreply@anthropic.com>
…nner keys

Scoped to runner-origin (tinyhumansai#2882) i18n keys only — openhuman-skills keys
(skills.install/detail/resource/uninstall and the base skills.create.*) are
left as-is.

- remove 9 dead runner keys across all 14 locales (parity-safe): skills.tabs.runners,
  skills.dashboard.{title,scheduledHeading,emptyTitle,emptyBody,create,run,loadError},
  skills.new.placeholderBody — the standalone scheduled-skills dashboard + Runners tab
  were phased out
- retranslate the 3 live runner values to 'workflow' in en + all 13 locales:
  skills.dashboard.enable/disable, skills.new.title

Note: pnpm i18n:check / i18n:english:check remain red on PRE-EXISTING debt
(8 untranslated workflow-surface keys + ~1266 English-leak strings across
unrelated domains) that predates this work; not addressed here.

Co-Authored-By: Claude <noreply@anthropic.com>
…on capabilities

These three entries describe the openhuman-skills Composio connection surface
(skill-backed integration status + manual data sync), not the workflow runner.
An earlier blanket rename wrongly put 'Workflow' in their names/how-to while the
descriptions still said 'skill'. Restore the skill wording so the catalog matches
the feature it documents. (The 'workflows' in the Google/Notion/exchange entries
is a generic English word — left as-is.)

Co-Authored-By: Claude <noreply@anthropic.com>
… dead run_skill tool

The runner's Save Schedule path creates a cron *agent* job whose tick prompt
told the agent to call the run_skill tool — which no longer exists (renamed to
run_workflow). Scheduled workflow runs would mis-fire (agent asked to call a
tool not in its set). Point the prompt at run_workflow with an explicit
workflow_id; run_workflow still accepts the legacy skill_id alias so the inputs
shape is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
…s/new

URL vocabulary now matches the unified primitive. Updates the run/new route
paths, the detail-drawer 'Run workflow' CTA, and the ?skill=→?workflow= query
param (drawer write + runner read) in lockstep. The /settings/skills-runner
dev-options deep link is left unchanged.

typecheck clean; 33 affected specs green.

Co-Authored-By: Claude <noreply@anthropic.com>
…ches the core

Wire-level plumbing bug found by testing: all 10 workflow controllers registered
under namespace "skills" → methods resolved as openhuman.skills_* — but the FE
(skillsApi) and the JSON-RPC e2e call openhuman.workflows_*. Every FE workflow
call (list/create/run/install/uninstall/describe/read_run_log/recent_runs/
read_resource) was failing 'unknown method' against a real core; only unit tests
(which mock the RPC / call handlers directly) hid it. Rename the namespace so the
methods are openhuman.workflows_* end-to-end.

Also:
- workflows_create schema was missing the when_to_use param (added to the FE form
  + handler in the unification, never to the controller schema) → strict param
  validation rejected FE creates carrying when_to_use. Declare it.
- repair json_rpc_workflows_lifecycle_round_trip: it was agent_workflows-era
  (asserted phases + called dead workflows_read/workflows_phase). Rewrite for the
  runner model: create→list→describe→uninstall, with when_to_use verified via
  describe. Fix the stale skills::types::ToolResult import (now workflows::types).
- update the skills_uninstall_rpc_e2e test + about_app catalog description + schema
  namespace assertions to match.

Co-Authored-By: Claude <noreply@anthropic.com>
Extends the mock-LLM task e2e (real dispatcher + board + run; mocked LLM only)
on the task spine — card → dispatch → run → write-back:

- task_run_failure_resolves_card_to_blocked: a failing provider makes the
  detached run error; assert the card resolves to Blocked with a blocker reason
  (run_autonomous Err → write_back Blocked).
- redispatch_of_claimed_card_is_rejected: dispatch a Ready card (claims →
  InProgress), then re-dispatch the same stale snapshot; the re-load-before-claim
  rejects it without spawning a second run (the dedup that stops a re-triggered
  card double-running). Deterministic (the true-concurrent TOCTOU window noted in
  dispatch_card is a separate, timing-dependent limitation, not asserted here).

read_run_log left out of the task spine by design — a dispatched task reports via
card evidence/status, not the runner-page run log. 6/6 e2e pass.

Co-Authored-By: Claude <noreply@anthropic.com>
…ap, backstop)

Cover the runaway-spawn safety rails that previously had no test:
- acquire_await_rejects_the_same_key_reentrantly: same (workflow_id+inputs) key
  held up the stack → rejected (tight-loop guard); freed on drop.
- acquire_await_caps_concurrent_awaits: 8 held → 9th rejected (MAX_ACTIVE_AWAITS
  nesting/fan-out cap); a freed slot is reusable.
- account_spawn_trips_the_process_backstop: drives past TOTAL_SPAWN_BACKSTOP (500)
  and asserts it trips. Guards share a serial lock (process-global statics).

Co-Authored-By: Claude <noreply@anthropic.com>
@sanil-23 sanil-23 marked this pull request as ready for review June 4, 2026 13:16
…kflows-merge

# Conflicts:
#	app/src/lib/i18n/ar.ts
#	app/src/lib/i18n/bn.ts
#	app/src/lib/i18n/de.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
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/openhuman/workflows/ops_create.rs (1)

47-89: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject unsupported inputs[].type values before writing workflow.toml.

The docs here say only string, integer, and boolean are supported, but the create/update path never enforces that. A bad RPC caller can persist an arbitrary kind and the runner then has no defined control to render on rediscovery.

Suggested fix
 fn validate_inputs(inputs: &mut [WorkflowCreateInputDef]) -> Result<(), String> {
     let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
     for input in inputs.iter_mut() {
         let trimmed = input.name.trim();
         if trimmed.is_empty() {
             return Err("input name must not be empty".to_string());
         }
         if !seen.insert(trimmed.to_ascii_lowercase()) {
             return Err(format!("duplicate input name '{trimmed}'"));
         }
         let trimmed = trimmed.to_string();
         input.name = trimmed;
+        input.type_ = match input.type_.as_deref().map(str::trim).filter(|s| !s.is_empty()) {
+            Some("string" | "integer" | "boolean" as kind) => Some(kind.to_string()),
+            Some(other) => return Err(format!("unsupported input type '{other}'")),
+            None => None,
+        };
     }
     Ok(())
 }
🤖 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_create.rs` around lines 47 - 89, Reject and
validate unsupported input types before persisting workflow metadata: in the
create/update entrypoints (e.g., create_workflow, create_workflow_inner, and
workflows_update) iterate over CreateWorkflowParams.inputs (each
WorkflowCreateInputDef) and verify the type/kind is one of the allowed values
"string", "integer", or "boolean"; if any input has an unsupported type, return
a clear error (or reject the request) instead of writing
workflow.toml/skill.toml, ensuring the check runs both on create and update
paths so invalid kinds never get persisted.
src/core/event_bus/events_tests.rs (1)

214-242: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the legacy event domain backward-compatible here.

These assertions lock the workflow lifecycle events onto "workflow". If any existing filters, dashboards, or trigger rules still key on the historic "skill" domain, this codifies a breaking change with no alias or migration path.

Based on learnings, "When reviewing changes to DomainEvent::variant_name() (or other “stable string” identifiers used by event bus consumers), treat those strings as a compatibility surface."

🤖 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/core/event_bus/events_tests.rs` around lines 214 - 242, The test changes
lock workflow lifecycle events to the new "workflow" domain and remove the
historic "skill" alias, breaking backward compatibility; update the
domain-returning logic (e.g., DomainEvent::variant_name / DomainEvent::domain or
wherever event domain string is produced) so that WorkflowLoaded,
WorkflowStopped, WorkflowStartFailed, and WorkflowExecuted continue to advertise
the legacy "skill" alias in addition to "workflow" (for example by returning
both names, emitting an alias field, or mapping "skill" to "workflow" on
input/output), and adjust tests to assert the presence of the legacy alias so
existing filters/dashboards/triggers keep working.
src/openhuman/workflows/ops_install.rs (1)

251-266: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check all discovered user roots for slug collisions before writing.

This only guards ~/.openhuman/skills/<slug>, but discovery still loads ~/.openhuman/workflows/ and ~/.agents/skills/. If the slug already exists in one of those roots, this install succeeds into a shadowed path and the catalog can remain unchanged.

Collision guard sketch
-    let skills_root = home
-        .as_deref()
-        .ok_or_else(|| "write failed: unable to resolve home directory".to_string())?
-        .join(".openhuman")
-        .join("skills");
+    let home_dir = home
+        .as_deref()
+        .ok_or_else(|| "write failed: unable to resolve home directory".to_string())?;
+    for existing in [
+        home_dir.join(".openhuman").join("workflows").join(&slug),
+        home_dir.join(".openhuman").join("skills").join(&slug),
+        home_dir.join(".agents").join("skills").join(&slug),
+    ] {
+        if existing.exists() {
+            return Err(format!(
+                "workflow already installed as {slug:?} at {}",
+                existing.display()
+            ));
+        }
+    }
+    let skills_root = home_dir.join(".openhuman").join("skills");
🤖 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 251 - 266, The current
install only checks the single target_dir (skills_root.join(&slug)) for
existence, but you must check all discovered user skill roots to prevent
shadowing; replace the single exists() guard with a scan over all user-scope
roots (e.g. the same roots used by discovery such as the computed skills_root
plus the ~/.openhuman/workflows and ~/.agents/skills locations or by calling the
existing discover_workflows/discovery helper) and if any root.join(&slug) exists
return an Err that names the colliding path; ensure you still compute the
intended target_dir for the actual install but abort early if any
discovered_root.join(&slug).exists() is true (use slug, skills_root, target_dir
and the discovery helper names to locate where to add the loop).
♻️ Duplicate comments (2)
app/src/components/intelligence/WorkflowsTab.tsx (2)

211-223: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use workflow id (slug) for local uninstall reconciliation.

The onUninstalled callback filters the local list by result.name, but the uninstall flow is explicitly keyed by skill.id (the slug) because display name and slug can diverge. When they do, the removed card stays visible locally until refetch succeeds.

🔧 Proposed fix
       {uninstallCandidate && (
         <UninstallSkillConfirmDialog
           skill={uninstallCandidate}
           onClose={() => setUninstallCandidate(null)}
           onUninstalled={result => {
             log('uninstalled name=%s', result.name);
+            const removedId = uninstallCandidate.id;
             addToast({
               type: 'success',
               title: t('workflows.delete'),
               message: `"${result.name}" ${t('common.success')}`,
             });
-            setWorkflows(prev => prev.filter(s => s.id !== result.name));
+            setWorkflows(prev => prev.filter(s => s.id !== removedId));
             void refresh();
           }}
         />
       )}
🤖 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/intelligence/WorkflowsTab.tsx` around lines 211 - 223, The
local uninstall reconciliation currently filters workflows by result.name, which
can differ from the workflow slug used as the uninstall key; update the
onUninstalled handler in UninstallSkillConfirmDialog to remove the workflow
using the slug/id returned by the uninstall (use result.id or the property that
represents the workflow slug) — i.e., change the setWorkflows filter from s.id
!== result.name to s.id !== result.id (and keep logging/toast using result.name
if you want the display name intact).

51-62: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Error handling swallows backend failures.

The refresh() catch block returns [] and clears loading, causing the UI to show "no workflows installed" instead of surfacing the error. This hides outages and makes them appear as if the user has no workflows.

💡 Recommended fix

Add error state and render an error panel when backend calls fail:

+  const [loadError, setLoadError] = useState<string | null>(null);
+
   const refresh = useCallback(async (): Promise<SkillSummary[]> => {
     try {
       const list = await skillsApi.listSkills();
       log('listWorkflows ok count=%d', list.length);
+      setLoadError(null);
       setWorkflows(list);
       return list;
     } catch (err) {
-      log('listWorkflows error %s', err instanceof Error ? err.message : String(err));
+      const message = err instanceof Error ? err.message : String(err);
+      log('listWorkflows error %s', message);
+      setLoadError(message);
       return [];
     } finally {
       setLoading(false);
     }
   }, []);

Then render an error panel when loadError is set instead of the empty state.

🤖 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/intelligence/WorkflowsTab.tsx` around lines 51 - 62, The
refresh() catch currently swallows backend failures by returning [] and only
clearing loading; add a component-level error state (e.g., loadError with setter
setLoadError) and in refresh() setLoadError(err) inside the catch (and clear it
on success), keep finally to setLoading(false), and update the component render
logic to show an error panel when loadError is set instead of rendering the
empty "no workflows" state; reference the refresh() function, setWorkflows,
setLoading and introduce setLoadError/loadError to locate the changes.
🧹 Nitpick comments (3)
app/src/pages/WorkflowsRun.tsx (1)

17-52: ⚡ Quick win

Consider adding unit test coverage for the new page component.

The WorkflowsRun page includes navigation logic (conditional back navigation with fallback) that would benefit from unit test coverage to verify the fallback behavior and ensure the page renders correctly. Per guidelines, new behavior in app/src/ should have unit tests via Vitest.

A test could verify:

  • Renders with correct title and back button labels via t()
  • Back button triggers navigate(-1) when history exists
  • Back button navigates to /intelligence?tab=workflows when no history
  • WorkflowRunnerBody is mounted
🤖 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/pages/WorkflowsRun.tsx` around lines 17 - 52, Add Vitest unit tests
for the WorkflowsRun component to exercise the conditional back navigation and
rendering: write tests that render WorkflowsRun (mocking useT to return
predictable labels and mocking WorkflowRunnerBody if needed), assert the title
and back button text from t(), simulate clicking the back button with
window.history.length > 1 and verify the mocked navigate was called with -1,
then simulate window.history.length === 1 and verify navigate was called with
'/intelligence?tab=workflows'; also include a test that WorkflowRunnerBody is
mounted. Mock useNavigate, useT, and optionally stub WorkflowRunnerBody to
isolate behavior.
src/openhuman/agent_registry/agents/orchestrator/prompt.md (1)

229-234: 💤 Low value

Add language specifier to fenced code block.

The code block contains JSON but lacks a language specifier, which affects syntax highlighting and linter compliance.

📝 Proposed fix
-```
+```json
 "images": [
🤖 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/agent_registry/agents/orchestrator/prompt.md` around lines 229
- 234, The fenced code block containing the JSON "images" array needs a language
specifier for linters and highlighting; update the opening fence from ``` to
```json so the block that includes the "images" array and its objects (the
artifact/file entries and captions) is marked as JSON.
src/openhuman/workflows/bus.rs (1)

164-172: ⚡ Quick win

Rename the subscriber identity to the workflows domain.

TriggeredSkillSubscriber and name() still publish the old skills::triggered_skill label, so bus registration and grep-based tracing keep reporting the retired domain after the rename.

Rename sketch
-struct TriggeredSkillSubscriber {
+struct TriggeredWorkflowSubscriber {
     index: Arc<TriggeredWorkflowIndex>,
 }
 
 #[async_trait]
-impl EventHandler for TriggeredSkillSubscriber {
+impl EventHandler for TriggeredWorkflowSubscriber {
     fn name(&self) -> &str {
-        "skills::triggered_skill"
+        "workflows::triggered_workflow"
     }

As per coding guidelines, "Name the handler struct <Purpose>Subscriber and the name() return value "<domain>::<purpose>" for grep-friendly tracing output."

🤖 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/bus.rs` around lines 164 - 172, The subscriber struct
and its name() still use the old skills domain; rename the struct
TriggeredSkillSubscriber to TriggeredWorkflowSubscriber and update its name() to
return "workflows::triggered_skill" (i.e., change the returned string from
"skills::triggered_skill" to "workflows::triggered_skill") so grep-friendly
tracing uses the workflows domain while preserving the purpose label.
🤖 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/pages/WorkflowsRun.tsx`:
- Around line 33-35: The onClick handler in WorkflowsRun.tsx currently uses
window.history.length > 1 to decide between navigate(-1) and
navigate('/intelligence?tab=workflows'), which can still go back to an external
referrer; change the guard to check the in-app history index
(window.history.state?.idx > 0) like useSettingsNavigation does, and only call
navigate(-1) when that condition is true, otherwise navigate to
'/intelligence?tab=workflows' — update the onClick that references navigate(-1)
and window.history.length accordingly to use window.history.state?.idx > 0.

In `@src/openhuman/about_app/types.rs`:
- Around line 11-12: The Workflows enum variant currently has #[serde(rename =
"workflows")] but lacks a backward-compatible alias; add #[serde(alias =
"skills")] next to the rename on the Workflows variant (and repeat the same
alias change for the other affected variant referenced at the 71-71 location) so
serde will accept the legacy "skills" string during deserialization, and
update/add serde tests to assert that deserializing JSON with "skills" yields
the Workflows variant (and likewise for the other affected variant).

In `@src/openhuman/workflows/ops_create.rs`:
- Around line 281-317: When params.overwrite is true, don't call
render_workflow_md on parse failure (which overwrites the user's existing body);
instead, if super::ops_parse::parse_workflow_md(&workflow_md_path) and
super::ops_parse::parse_workflow_md(&legacy_md_path) both return None, read the
raw file contents from workflow_md_path (or legacy_md_path if workflow absent)
and return Some(raw_body) so the existing body is preserved and later combined
with render_workflow_frontmatter; update the preserved_body construction (the
branch assigning preserved_body) to implement this non-frontmatter fallback and
avoid calling render_workflow_md in that overwrite path.

In `@src/openhuman/workflows/ops_discover.rs`:
- Around line 223-233: The discovery currently uses Path::exists which follows
symlinks; update the checks for workflow_md, legacy_md, and legacy_manifest to
reject symlinks before loading: for each path (workflow_md, legacy_md,
legacy_manifest) call std::fs::symlink_metadata(...) and skip the path if
metadata.file_type().is_symlink() (or if symlink_metadata returns an error),
only then check existence/readability and call load_from_workflow_md (or
relevant loader). Reference symbols: workflow_md, legacy_md, legacy_manifest,
load_from_workflow_md, and use std::fs::symlink_metadata /
std::os::unix::fs::FileTypeExt/file_type().is_symlink() to implement the safe
check.

In `@src/openhuman/workflows/ops_install.rs`:
- Around line 408-421: The uninstall resolver only checks ~/.openhuman/workflows
and ~/.openhuman/skills so workflows discovered under ~/.agents/skills never
match and cannot be uninstalled; update the root-resolution logic in
ops_install.rs (the block that builds and .find(...)s roots for the trimmed id)
to include home.join(".agents").join("skills") alongside the existing openhuman
paths, so the same trimmed lookup used by discover_workflows_inner() will locate
workflows under ~/.agents/skills as well; keep the same existence check
(r.join(&trimmed).exists()) and error path unchanged.

In `@src/openhuman/workflows/run_log.rs`:
- Around line 24-66: Add debug-level logging to the cancellation registry: in
register_run_cancel(log the run_id and that a token was created/inserted and
whether it replaced an existing token), in unregister_run_cancel(log the run_id
and whether an entry was removed), and in cancel_run(log the run_id and whether
a token was found and cancelled or missed). Use the same logging crate used
elsewhere (debug! or trace!) and include the run_id and branch decisions
(created/inserted, replaced, removed, cancelled, missed) in the messages so
state transitions in register_run_cancel, unregister_run_cancel, and cancel_run
are observable without changing behavior.

---

Outside diff comments:
In `@src/core/event_bus/events_tests.rs`:
- Around line 214-242: The test changes lock workflow lifecycle events to the
new "workflow" domain and remove the historic "skill" alias, breaking backward
compatibility; update the domain-returning logic (e.g.,
DomainEvent::variant_name / DomainEvent::domain or wherever event domain string
is produced) so that WorkflowLoaded, WorkflowStopped, WorkflowStartFailed, and
WorkflowExecuted continue to advertise the legacy "skill" alias in addition to
"workflow" (for example by returning both names, emitting an alias field, or
mapping "skill" to "workflow" on input/output), and adjust tests to assert the
presence of the legacy alias so existing filters/dashboards/triggers keep
working.

In `@src/openhuman/workflows/ops_create.rs`:
- Around line 47-89: Reject and validate unsupported input types before
persisting workflow metadata: in the create/update entrypoints (e.g.,
create_workflow, create_workflow_inner, and workflows_update) iterate over
CreateWorkflowParams.inputs (each WorkflowCreateInputDef) and verify the
type/kind is one of the allowed values "string", "integer", or "boolean"; if any
input has an unsupported type, return a clear error (or reject the request)
instead of writing workflow.toml/skill.toml, ensuring the check runs both on
create and update paths so invalid kinds never get persisted.

In `@src/openhuman/workflows/ops_install.rs`:
- Around line 251-266: The current install only checks the single target_dir
(skills_root.join(&slug)) for existence, but you must check all discovered user
skill roots to prevent shadowing; replace the single exists() guard with a scan
over all user-scope roots (e.g. the same roots used by discovery such as the
computed skills_root plus the ~/.openhuman/workflows and ~/.agents/skills
locations or by calling the existing discover_workflows/discovery helper) and if
any root.join(&slug) exists return an Err that names the colliding path; ensure
you still compute the intended target_dir for the actual install but abort early
if any discovered_root.join(&slug).exists() is true (use slug, skills_root,
target_dir and the discovery helper names to locate where to add the loop).

---

Duplicate comments:
In `@app/src/components/intelligence/WorkflowsTab.tsx`:
- Around line 211-223: The local uninstall reconciliation currently filters
workflows by result.name, which can differ from the workflow slug used as the
uninstall key; update the onUninstalled handler in UninstallSkillConfirmDialog
to remove the workflow using the slug/id returned by the uninstall (use
result.id or the property that represents the workflow slug) — i.e., change the
setWorkflows filter from s.id !== result.name to s.id !== result.id (and keep
logging/toast using result.name if you want the display name intact).
- Around line 51-62: The refresh() catch currently swallows backend failures by
returning [] and only clearing loading; add a component-level error state (e.g.,
loadError with setter setLoadError) and in refresh() setLoadError(err) inside
the catch (and clear it on success), keep finally to setLoading(false), and
update the component render logic to show an error panel when loadError is set
instead of rendering the empty "no workflows" state; reference the refresh()
function, setWorkflows, setLoading and introduce setLoadError/loadError to
locate the changes.

---

Nitpick comments:
In `@app/src/pages/WorkflowsRun.tsx`:
- Around line 17-52: Add Vitest unit tests for the WorkflowsRun component to
exercise the conditional back navigation and rendering: write tests that render
WorkflowsRun (mocking useT to return predictable labels and mocking
WorkflowRunnerBody if needed), assert the title and back button text from t(),
simulate clicking the back button with window.history.length > 1 and verify the
mocked navigate was called with -1, then simulate window.history.length === 1
and verify navigate was called with '/intelligence?tab=workflows'; also include
a test that WorkflowRunnerBody is mounted. Mock useNavigate, useT, and
optionally stub WorkflowRunnerBody to isolate behavior.

In `@src/openhuman/agent_registry/agents/orchestrator/prompt.md`:
- Around line 229-234: The fenced code block containing the JSON "images" array
needs a language specifier for linters and highlighting; update the opening
fence from ``` to ```json so the block that includes the "images" array and its
objects (the artifact/file entries and captions) is marked as JSON.

In `@src/openhuman/workflows/bus.rs`:
- Around line 164-172: The subscriber struct and its name() still use the old
skills domain; rename the struct TriggeredSkillSubscriber to
TriggeredWorkflowSubscriber and update its name() to return
"workflows::triggered_skill" (i.e., change the returned string from
"skills::triggered_skill" to "workflows::triggered_skill") so grep-friendly
tracing uses the workflows domain while preserving the purpose label.
🪄 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: 26f02882-fbf4-4ece-a68e-f6badecd8269

📥 Commits

Reviewing files that changed from the base of the PR and between d000b5c and 0cfdcb2.

📒 Files selected for processing (82)
  • app/src/components/intelligence/WorkflowsTab.tsx
  • app/src/components/intelligence/__tests__/WorkflowsTab.test.tsx
  • app/src/components/skills/CreateSkillModal.tsx
  • app/src/components/skills/CreateWorkflowForm.tsx
  • app/src/components/skills/SkillDetailDrawer.tsx
  • app/src/components/skills/SkillResourcePreview.tsx
  • app/src/components/skills/UninstallSkillConfirmDialog.tsx
  • app/src/components/skills/WorkflowRunnerBody.tsx
  • app/src/components/skills/__tests__/CreateSkillModal.test.tsx
  • app/src/components/skills/__tests__/SkillDetailDrawer.test.tsx
  • app/src/components/skills/__tests__/SkillResourcePreview.test.tsx
  • app/src/components/skills/__tests__/UninstallSkillConfirmDialog.test.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
  • app/src/pages/Intelligence.tsx
  • app/src/pages/WorkflowsRun.tsx
  • app/src/services/api/skillsApi.ts
  • src/core/event_bus/events.rs
  • src/core/event_bus/events_tests.rs
  • src/core/jsonrpc.rs
  • src/openhuman/about_app/README.md
  • src/openhuman/about_app/catalog_data.rs
  • src/openhuman/about_app/catalog_tests.rs
  • src/openhuman/about_app/types.rs
  • src/openhuman/agent/harness/session/builder.rs
  • src/openhuman/agent/harness/session/runtime.rs
  • src/openhuman/agent/harness/session/turn.rs
  • src/openhuman/agent/harness/session/types.rs
  • src/openhuman/agent/harness/subagent_runner/ops.rs
  • src/openhuman/agent/profiles.rs
  • src/openhuman/agent/task_dispatcher.rs
  • src/openhuman/agent/tools.rs
  • src/openhuman/agent/tools/run_workflow.rs
  • src/openhuman/agent_registry/agents/orchestrator/agent.toml
  • src/openhuman/agent_registry/agents/orchestrator/prompt.md
  • src/openhuman/channels/runtime/startup.rs
  • src/openhuman/composio/tools/direct.rs
  • src/openhuman/composio/tools/direct_tests.rs
  • src/openhuman/inference/provider/factory.rs
  • src/openhuman/todos/ops.rs
  • src/openhuman/tools/impl/presentation/tests.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/user_filter.rs
  • src/openhuman/workflows/bus.rs
  • src/openhuman/workflows/e2e_plumbing_tests.rs
  • src/openhuman/workflows/e2e_run_tests.rs
  • src/openhuman/workflows/inject.rs
  • src/openhuman/workflows/ops.rs
  • src/openhuman/workflows/ops_create.rs
  • src/openhuman/workflows/ops_discover.rs
  • src/openhuman/workflows/ops_install.rs
  • src/openhuman/workflows/ops_parse.rs
  • src/openhuman/workflows/ops_tests.rs
  • src/openhuman/workflows/ops_types.rs
  • src/openhuman/workflows/registry.rs
  • src/openhuman/workflows/run_log.rs
  • src/openhuman/workflows/schemas.rs
  • src/openhuman/workflows/tools.rs
  • src/openhuman/workspace/ops.rs
  • tests/agent_harness_leftovers_raw_coverage_e2e.rs
  • tests/agent_prompts_subagent_raw_coverage_e2e.rs
  • tests/agent_round26_raw_coverage_e2e.rs
  • tests/agent_session_round24_raw_coverage_e2e.rs
  • tests/composio_raw_coverage_e2e.rs
  • tests/inference_agent_raw_coverage_e2e.rs
  • tests/json_rpc_e2e.rs
  • tests/personality_e2e.rs
  • tests/tools_approval_channels_raw_coverage_e2e.rs
  • tests/tools_channels_raw_coverage_e2e.rs
💤 Files with no reviewable changes (4)
  • app/src/components/skills/SkillResourcePreview.tsx
  • app/src/components/skills/tests/SkillResourcePreview.test.tsx
  • app/src/components/skills/SkillDetailDrawer.tsx
  • app/src/components/skills/tests/SkillDetailDrawer.test.tsx
✅ Files skipped from review due to trivial changes (4)
  • src/openhuman/about_app/README.md
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/it.ts
🚧 Files skipped from review as they are similar to previous changes (31)
  • src/openhuman/tools/impl/presentation/tests.rs
  • src/openhuman/agent/task_dispatcher.rs
  • src/openhuman/composio/tools/direct.rs
  • app/src/components/intelligence/tests/WorkflowsTab.test.tsx
  • src/openhuman/agent/harness/session/runtime.rs
  • src/openhuman/workflows/e2e_plumbing_tests.rs
  • src/openhuman/channels/runtime/startup.rs
  • app/src/lib/i18n/bn.ts
  • src/openhuman/composio/tools/direct_tests.rs
  • app/src/lib/i18n/zh-CN.ts
  • src/openhuman/agent_registry/agents/orchestrator/agent.toml
  • src/openhuman/agent/harness/session/types.rs
  • src/openhuman/todos/ops.rs
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/es.ts
  • src/openhuman/agent/harness/session/builder.rs
  • src/openhuman/inference/provider/factory.rs
  • app/src/pages/Intelligence.tsx
  • app/src/lib/i18n/pl.ts
  • src/openhuman/agent/harness/subagent_runner/ops.rs
  • src/openhuman/workflows/e2e_run_tests.rs
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/pt.ts
  • src/openhuman/agent/profiles.rs
  • src/openhuman/agent/harness/session/turn.rs
  • app/src/lib/i18n/ko.ts
  • src/openhuman/agent/tools/run_workflow.rs
  • app/src/lib/i18n/ru.ts
  • src/openhuman/agent/tools.rs
  • src/openhuman/workflows/ops_tests.rs
  • src/openhuman/workflows/schemas.rs

Comment thread app/src/pages/WorkflowsRun.tsx
Comment thread src/openhuman/about_app/types.rs Outdated
Comment thread src/openhuman/workflows/ops_create.rs
Comment thread src/openhuman/workflows/ops_discover.rs
Comment thread src/openhuman/workflows/ops_install.rs
Comment thread src/openhuman/workflows/run_log.rs
Core:
- ops_create: refuse an edit (overwrite) when the existing body can't be
  parsed instead of clobbering it with the scaffold template (data loss).
- ops_install: uninstall now also resolves ~/.agents/skills/, the legacy
  user root discovery surfaces — else a listed workflow can't be deleted.
- ops_discover: reject symlinked manifests (WORKFLOW.md/SKILL.md/skill.json)
  before loading; exists() follows symlinks → local-file disclosure path.
- run_log: read_terminal_outcome now requires the closing `finished:` line,
  so a half-written footer isn't read as terminal. Plus debug logs around
  the run-cancel register/cancel/unregister transitions.
- user_filter: keep `skill_manage` as a legacy alias for `workflow_manage`
  so persisted enabled_tool_names snapshots don't silently drop the tools.
- startup: install the real triggered-workflow subscriber after discovery
  (was a no-op shim, so `triggers:` were ignored in the channel runtime).

Frontend:
- WorkflowsTab: surface listSkills() failures as an error/retry panel
  instead of the "no workflows" empty state; reconcile uninstall by the
  workflow id/slug, not the display name.
- Intelligence: validate `?tab=` against the visible tab set so a forged
  `?tab=council` can't open a dev-only tab in prod.

Tests: edit-refuses-unparseable-body, uninstall-resolves-agents-skills,
symlinked-manifest-rejected, terminal-outcome-requires-finished.

Skipped (deferred by maintainer): run_workflow spawn-backstop counting,
event_bus variant_name stability, and the about_app catalog updates.

Co-Authored-By: Claude <noreply@anthropic.com>
@sanil-23
Copy link
Copy Markdown
Contributor Author

sanil-23 commented Jun 4, 2026

Addressed CodeRabbit's review in 324d0203 — the actionable bugs in the new code:

Core

  • ops_create: edit now refuses rather than overwriting an unparseable body with the scaffold (data-loss guard).
  • ops_install: uninstall also resolves ~/.agents/skills/ (the legacy user root discovery surfaces).
  • ops_discover: reject symlinked WORKFLOW.md/SKILL.md/skill.json before loading (symlink-follow disclosure path).
  • run_log: read_terminal_outcome now requires the closing finished: line; added debug logs to the run-cancel registry.
  • user_filter: kept skill_manage as a legacy alias for workflow_manage (persisted prefs back-compat).
  • startup: wired the real triggered-workflow subscriber after discovery (was a no-op shim, so triggers: were ignored).

Frontend

  • WorkflowsTab: listSkills() failures render an error/retry panel instead of the empty state; uninstall reconciles by id/slug not display name.
  • Intelligence: ?tab= validated against the visible tab set so a forged ?tab=council can't open the dev-only tab in prod.

New tests: edit-refuses-unparseable-body, uninstall-resolves-~/.agents/skills, symlinked-manifest-rejected, terminal-outcome-requires-finished.

Deferred (per @senamakel on this migration PR): run_workflow spawn-backstop counting, event_bus variant_name() stability, and the about_app/ capability-catalog updates.

sanil-23 and others added 3 commits June 4, 2026 19:44
Two runner bugs:
- Run-now could spawn two runs from rapid clicks: the spawn RPC returns in
  ~5ms so the re-entry guard released before a second click ~0.5s later.
  Now the guard is held through a 2.5s cooldown after a successful spawn
  (released immediately on error) and mirrored into a `runBusy` state that
  disables the Run buttons, so an accidental double-click can't duplicate.
- "Run" on a saved schedule went through cron_run → agent prompt →
  run_workflow: no visible run appeared and repeat clicks silently errored
  "cron job already running". It now runs the workflow directly with that
  schedule's snapshotted inputs (parseScheduledInputs), so a real run shows
  in Recent runs immediately; errors surface in the schedule error pill.

Co-Authored-By: Claude <noreply@anthropic.com>
…kflows-merge

# Conflicts:
#	src/openhuman/agent_registry/agents/orchestrator/agent.toml
#	src/openhuman/agent_registry/agents/orchestrator/prompt.md
…ents

The upstream merge brought three new agents (scheduler / presentation /
desktop_control) whose prompt fixtures set both `skills: &[]` and
`workflows: &[]`. This PR dissolved the `agent_workflows` domain, so
PromptContext no longer has the `workflows` field (only `skills`, now
typed `&[Workflow]`). Drop the stale `workflows: &[]` line so the agents
compile. (Renaming the surviving `skills` field → `workflows` is a
separate follow-up PR.)

Co-Authored-By: Claude <noreply@anthropic.com>
@sanil-23
Copy link
Copy Markdown
Contributor Author

sanil-23 commented Jun 4, 2026

@coderabbitai review

Pushed fixes for the prior review (commit 324d020) plus an upstream re-merge (ca207d6). Addressed in-code: the edit body-clobber guard (ops_create), uninstall reaching ~/.agents/skills (ops_install), symlink-manifest rejection (ops_discover), terminal-outcome footer check + run-cancel debug logs (run_log), skill_manage legacy alias (user_filter), triggered-workflow subscriber wiring (startup), and the FE listSkills error panel / uninstall-by-id / tab-guard. Could you re-scan and resolve the threads that are now addressed? The agent_workflows variant_name/domain + about_app catalog items remain deferred per @senamakel for this migration PR.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all review comments (unresolved) on PR `#3324`
gh api graphql -f query='
{
  repository(owner: "tinyhumansai", name: "openhuman") {
    pullRequest(number: 3324) {
      reviewThreads(first: 50) {
        nodes {
          id
          isResolved
          comments(first: 1) {
            nodes {
              id
              databaseId
              body
              author { login }
              path
              line
            }
          }
        }
      }
    }
  }
}' | jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {threadId: .id, path: .comments.nodes[0].path, line: .comments.nodes[0].line, author: .comments.nodes[0].author.login, body: .comments.nodes[0].body[:200]}'

Length of output: 1502


🏁 Script executed:

#!/bin/bash
# Get full body of both unresolved threads
gh api graphql -f query='
{
  repository(owner: "tinyhumansai", name: "openhuman") {
    pullRequest(number: 3324) {
      reviewThreads(first: 50) {
        nodes {
          id
          isResolved
          comments(first: 3) {
            nodes {
              databaseId
              body
              author { login }
            }
          }
        }
      }
    }
  }
}' | jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)'

echo "---FILE CONTENT---"
# Check current WorkflowsRun.tsx for the navigation pattern
rg -n "history|navigate" app/src/pages/WorkflowsRun.tsx

Length of output: 5631



@sanil-23 — thanks for the detailed summary. I re-scanned both remaining open threads and here's the status:


🟡 Still open — fix not yet applied

app/src/pages/WorkflowsRun.tsx line 34 — back-navigation guard

The code still reads:

window.history.length > 1 ? navigate(-1) : navigate('/intelligence?tab=workflows')

The suggested fix (align with useSettingsNavigation's safer window.history.state?.idx > 0 guard) was not picked up in commit 324d0203. The original concern still applies — history.length > 1 fires even when the first entry is an external referrer, potentially bouncing the user out of the app. Quick one-liner fix:

window.history.state?.idx > 0 ? navigate(-1) : navigate('/intelligence?tab=workflows')

🔵 Intentionally deferred — keeping thread open as a reminder

src/openhuman/about_app/types.rs line 12#[serde(alias = "skills")] on the Workflows variant

Acknowledged as deferred per @senamakel for this migration PR (same scope as the event_bus variant_name() stability item). Thread stays open so it isn't forgotten in the follow-up.


Triggering a full re-review of the pushed commits now.


🧠 Learnings used
Learnt from: oxoxDev
Repo: tinyhumansai/openhuman PR: 833
File: app/src/pages/Skills.tsx:831-857
Timestamp: 2026-04-24T11:38:02.319Z
Learning: In `app/src/pages/Skills.tsx` and `app/src/components/skills/UninstallSkillConfirmDialog.tsx` (PR `#833`), the page-level integration test for the uninstall flow (`Skills.uninstall.test.tsx`) was intentionally deferred to a follow-up. The planned test should: mount the full Skills page using the `renderWithProviders` harness (same pattern as `Skills.composio-catalog.test.tsx`), click through the skill card → uninstall overflow menu → UninstallSkillConfirmDialog → confirm, and assert: `discoveredSkills` row is removed from the grid, detail drawer selection is cleared when it matched the removed skill, a success toast appears via ToastContainer, and `refreshDiscoveredSkills` is invoked. The blocker was the page harness setup time; the slug regression (id vs name) was already covered in `UninstallSkillConfirmDialog.test.tsx` commit b160a8ef.

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 3324
File: src/core/event_bus/events.rs:1080-1080
Timestamp: 2026-06-04T05:02:47.951Z
Learning: In `tinyhumansai/openhuman` PR `#3324`, the `DomainEvent::variant_name()` rename in `src/core/event_bus/events.rs` from `SkillLoaded`/`SkillStopped`/`SkillStartFailed`/`SkillExecuted` to `WorkflowLoaded`/`WorkflowStopped`/`WorkflowStartFailed`/`WorkflowExecuted` was intentionally deferred as a compatibility follow-up. Future reviews should consider downstream persisted filters, dashboards, or trigger rules that may still key off the old stable `variant_name()` strings and may require an alias or migration path.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-02T22:01:29.371Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md). This checklist is required for remote agents because OpenHuman has several merge gates: Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md. This checklist is required because OpenHuman has merge gates for Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependencies.

Learnt from: graycyrus
Repo: tinyhumansai/openhuman PR: 283
File: app/test/e2e/specs/skill-execution-flow.spec.ts:67-114
Timestamp: 2026-04-03T06:28:58.632Z
Learning: In `app/test/e2e/specs/skill-execution-flow.spec.ts`, the model→agent→tool→conversation E2E path (model-triggered tool_calls) is intentionally deferred to a future PR. It is tracked via an `it.skip` placeholder at the end of the spec. The blocker is that the mock backend does not yet support returning structured `tool_calls` in chat completion responses. The current spec only covers the deterministic skill runtime RPC surface: `skills_start` → `skills_list_tools` → `skills_call_tool` → `skills_stop`.

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 1455
File: scripts/work/start.sh:150-154
Timestamp: 2026-05-10T03:14:32.586Z
Learning: In `scripts/work/start.sh` (repo: tinyhumansai/openhuman), `--agent codex` unconditionally invokes `codex exec --dangerously-bypass-approvals-and-sandbox`. This is intentional by design: the wrapper is a local contributor handoff tool and yolo/bypass mode was explicitly requested by the maintainer. The prompt already includes an instruction to treat the GitHub issue body as untrusted content. Do not flag this as a security issue in future reviews.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T21:18:08.576Z
Learning: Before opening AI-authored PRs from Codex web or Linear-launched agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md` and report any blocked commands with exact error messages instead of claiming validation passed

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T21:18:08.576Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md); this checklist is required because OpenHuman has several merge gates that are easy to miss in partial environments

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T04:47:45.876Z
Learning: Follow the Codex PR checklist (`docs/agent-workflows/codex-pr-checklist.md`) before opening AI-authored PRs; validate Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T21:50:55.026Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T05:05:50.773Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md`

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T21:50:55.026Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow `docs/agent-workflows/codex-pr-checklist.md`; the checklist is required for remote agents
✅ Action performed

Full review finished.

CodeRabbit: `window.history.length > 1` is true even when the only prior
entry is an external referrer, so Back could bounce the user out of the
app. Use the router history index (`window.history.state?.idx > 0`) — the
same guard useSettingsNavigation uses — so navigate(-1) only fires when
there's an in-app entry; otherwise fall back to the Workflows tab.

Co-Authored-By: Claude <noreply@anthropic.com>
@sanil-23
Copy link
Copy Markdown
Contributor Author

sanil-23 commented Jun 4, 2026

Thanks for the re-scan @coderabbitai.

Applied (commit b45d317) — WorkflowsRun.tsx Back-nav now guards on the router history index instead of history.length:

(window.history.state?.idx ?? 0) > 0 ? navigate(-1) : navigate('/intelligence?tab=workflows')

so it won't bounce out of the app when the only prior entry is an external referrer (matches useSettingsNavigation's guard).

🔵 Deferred to a follow-up PR (out of scope for this migration, per @senamakel): the #[serde(alias = "skills")] back-compat on about_app::CapabilityCategory, the event_bus::variant_name()/domain() stability, the run_workflow spawn-backstop counting, and completing the internal skillsworkflows identifier rename (the surviving PromptContext.skills field etc.). Keeping those threads open as reminders — they'll be picked up together in the rename PR.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

Tip

For best results, initiate chat on the files or code changes.

@sanil-23 — confirmed on both counts, thanks for the clean summary.

WorkflowsRun.tsx back-nav — fix verified. Using window.history.state?.idx ?? 0 matches the useSettingsNavigation pattern and safely handles the external-referrer edge case. Resolving that thread.

🔵 Deferred follow-up items — noted. I'll record the new ones below so they aren't lost across PRs. Would you like me to open a GitHub issue to track them together?

🐇 ✨


✏️ Learnings added
Learnt from: sanil-23
URL: https://github.com/tinyhumansai/openhuman/pull/3324

Timestamp: 2026-06-04T15:15:38.156Z
Learning: In `tinyhumansai/openhuman` PR `#3324`, the `#[serde(alias = "skills")]` back-compat attribute on `CapabilityCategory::Workflows` in `src/openhuman/about_app/types.rs` was intentionally deferred to a follow-up rename PR (per senamakel). The alias ensures external callers that serialise `"skills"` continue to deserialise into `Workflows` after the rename; it should be added at the same time as the other `skills`→`workflows` identifier renames.

Learnt from: sanil-23
URL: https://github.com/tinyhumansai/openhuman/pull/3324

Timestamp: 2026-06-04T15:15:38.156Z
Learning: In `tinyhumansai/openhuman` PR `#3324`, the `run_workflow` spawn-backstop counter correctness (ensuring the atomic backstop counter is decremented on every code path, including early-return errors before the background task is spawned) was intentionally deferred to a follow-up PR per senamakel. Future reviews should check that `SPAWN_BACKSTOP_COUNT` is decremented in all error/early-exit branches in `src/openhuman/agent/tools/run_workflow.rs`.

Learnt from: sanil-23
URL: https://github.com/tinyhumansai/openhuman/pull/3324

Timestamp: 2026-06-04T15:15:38.156Z
Learning: In `tinyhumansai/openhuman` PR `#3324`, completing the internal `skills`→`workflows` identifier rename (e.g., the surviving `PromptContext.skills` field in `src/openhuman/agent/prompts/types.rs`, `AgentBuilder::skills()` in the session builder, and `Agent::skills()` accessor) was intentionally deferred to a dedicated rename follow-up PR per senamakel. These symbols keep `skills` in their Rust names while holding `Workflow` values; the rename should be done in one sweep to minimise churn.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 3324
File: src/core/event_bus/events.rs:1080-1080
Timestamp: 2026-06-04T05:02:47.951Z
Learning: In `tinyhumansai/openhuman` PR `#3324`, the `DomainEvent::variant_name()` rename in `src/core/event_bus/events.rs` from `SkillLoaded`/`SkillStopped`/`SkillStartFailed`/`SkillExecuted` to `WorkflowLoaded`/`WorkflowStopped`/`WorkflowStartFailed`/`WorkflowExecuted` was intentionally deferred as a compatibility follow-up. Future reviews should consider downstream persisted filters, dashboards, or trigger rules that may still key off the old stable `variant_name()` strings and may require an alias or migration path.

Learnt from: oxoxDev
Repo: tinyhumansai/openhuman PR: 833
File: app/src/pages/Skills.tsx:831-857
Timestamp: 2026-04-24T11:38:02.319Z
Learning: In `app/src/pages/Skills.tsx` and `app/src/components/skills/UninstallSkillConfirmDialog.tsx` (PR `#833`), the page-level integration test for the uninstall flow (`Skills.uninstall.test.tsx`) was intentionally deferred to a follow-up. The planned test should: mount the full Skills page using the `renderWithProviders` harness (same pattern as `Skills.composio-catalog.test.tsx`), click through the skill card → uninstall overflow menu → UninstallSkillConfirmDialog → confirm, and assert: `discoveredSkills` row is removed from the grid, detail drawer selection is cleared when it matched the removed skill, a success toast appears via ToastContainer, and `refreshDiscoveredSkills` is invoked. The blocker was the page harness setup time; the slug regression (id vs name) was already covered in `UninstallSkillConfirmDialog.test.tsx` commit b160a8ef.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-02T22:01:29.371Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md). This checklist is required for remote agents because OpenHuman has several merge gates: Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md. This checklist is required because OpenHuman has merge gates for Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependencies.

Learnt from: graycyrus
Repo: tinyhumansai/openhuman PR: 2703
File: app/src/components/settings/panels/DevWorkflowPanel.tsx:55-71
Timestamp: 2026-05-27T14:39:40.859Z
Learning: In `app/src/components/settings/panels/DevWorkflowPanel.tsx`, the `DevWorkflowConfig` is intentionally persisted via ad-hoc `localStorage` (key `openhuman:dev-workflow-config`) as a Phase 1 temporary stopgap. Phase 2 (tracked in PR `#2707`) will migrate this config to the Rust core via the `skills_run` RPC. A Redux Toolkit slice was deliberately skipped to avoid churn on a short-lived intermediate layer. Do not flag this localStorage usage as a violation of the Redux-persist guideline.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T04:47:45.876Z
Learning: Follow the Codex PR checklist (`docs/agent-workflows/codex-pr-checklist.md`) before opening AI-authored PRs; validate Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability

Learnt from: graycyrus
Repo: tinyhumansai/openhuman PR: 283
File: app/test/e2e/specs/skill-execution-flow.spec.ts:67-114
Timestamp: 2026-04-03T06:28:58.632Z
Learning: In `app/test/e2e/specs/skill-execution-flow.spec.ts`, the model→agent→tool→conversation E2E path (model-triggered tool_calls) is intentionally deferred to a future PR. It is tracked via an `it.skip` placeholder at the end of the spec. The blocker is that the mock backend does not yet support returning structured `tool_calls` in chat completion responses. The current spec only covers the deterministic skill runtime RPC surface: `skills_start` → `skills_list_tools` → `skills_call_tool` → `skills_stop`.

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 2112
File: app/src-tauri/permissions/allow-core-process.toml:103-122
Timestamp: 2026-05-20T02:56:09.546Z
Learning: In the `tinyhumansai/openhuman` repository, splitting `logs_folder_path`/`reveal_logs_folder` and `meet_call_open_window`/`meet_call_close_window` out of the `allow-core-process` permission bundle (`app/src-tauri/permissions/allow-core-process.toml`) into dedicated narrower permission identifiers (e.g., `allow-logs-access` and `allow-meet-window-control`) is a known follow-up task (deferred from PR `#2112`). It requires: (1) creating new permission TOML files, (2) updating capability grants in `app/src-tauri/capabilities/` to bind them only to the UI surfaces that need them, and (3) verifying no `allow-core-process` consumer regresses.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T21:18:08.576Z
Learning: Before opening AI-authored PRs from Codex web or Linear-launched agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md` and report any blocked commands with exact error messages instead of claiming validation passed

Learnt from: sanil-23
Repo: tinyhumansai/openhuman PR: 1825
File: src/openhuman/composio/tools.rs:1028-1039
Timestamp: 2026-05-15T22:00:15.051Z
Learning: In `src/openhuman/composio/tools.rs` (PR `#1825`), the stale `Arc<Config>` snapshot concern was resolved in commit `18ced400` (Wave 4). Each tool's `execute()` method now calls `config_rpc::load_config_with_timeout()` at the top of every invocation to reload the live on-disk config before calling `create_composio_client()`. The `Arc<Config>` held by `self` (captured at agent-init via `Arc::new(config.clone())`) is no longer used for mode selection or key lookup — it is purely an init-time reference. The same per-call reload pattern was applied to `ProviderContext::execute()` and `ProviderContext::backend_client()` in `src/openhuman/composio/providers/types.rs`. 13 tests were migrated to the `TEST_ENV_LOCK` + `OPENHUMAN_WORKSPACE`-tempdir + `config.save()` isolation pattern to ensure `load_config_with_timeout()` reads a controlled snapshot in tests.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Applies to src/openhuman/about_app/**/*.rs : Update `src/openhuman/about_app/` (capability catalog) when a change adds, removes, renames, relocates, or materially changes a user-facing feature.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Applies to src/openhuman/about_app/**/*.rs : Update `src/openhuman/about_app/` when a change adds, removes, renames, relocates, or materially changes a user-facing feature so the runtime capability catalog remains the source of truth

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Applies to src/openhuman/about_app/**/*.rs : Update src/openhuman/about_app/ capability catalog when a change adds, removes, renames, relocates, or materially changes a user-facing feature.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Applies to src/openhuman/about_app/**/*.rs : When a change adds, removes, renames, relocates, or materially changes a user-facing feature, update `src/openhuman/about_app/` in the same work so the runtime capability catalog remains the source of truth

sanil-23 and others added 10 commits June 4, 2026 23:37
The Playwright workflow CRUD spec broke on the runner consolidation:
- Creating a workflow now navigates to the runner page, so the delete
  step has to go back to /workflows (the list) first.
- The card's delete lives in the "More actions" menu — open it before
  clicking delete.
- Align selectors to what the components actually emit:
  workflow-card-<id>-delete → the menu's `workflow-uninstall-<id>`,
  `alertdialog` → `dialog`, `wf-delete-confirm-btn` → `uninstall-skill-confirm`.

Fixes the red "E2E web lane 4/4" shard (the Playwright E2E Gate was
already green — it tolerates one shard).

Co-Authored-By: Claude <noreply@anthropic.com>
…g-safe e2e

- Upstream merge added task_manager_agent (and other specialists) whose
  prompt fixture set the agent_workflows `workflows: &[]` field this PR
  removed; drop it (PromptContext keeps only the unified `skills:
  &[Workflow]`). Same one-line strip applied to each new agent per merge
  until this lands.
- E2E: the workflow create/delete spec uses a slug-safe name, since
  create stores `name: <slug>` and the runner heading shows that slug.

Co-Authored-By: Claude <noreply@anthropic.com>
…t probe

The create/delete spec failed at `expect(created?.id).toBeTruthy()` because
workflows_list returns the array under `skills` (the kept field name), not
`workflows`. Since the test name is already slug-shaped, the created id ===
name, so use it directly for the delete selectors and drop the RPC round-trip
(and its now-unused callCoreRpc import). Heading + final absence assertions
still cover create→delete.

Co-Authored-By: Claude <noreply@anthropic.com>
- orchestrator: drop `generate_presentation` from its direct tools —
  upstream moved deck policy to presentation_agent (delegate_make_presentation);
  tests/orchestrator_presentation_wiring asserts it's not listed directly.
- about_app catalog test: this PR renamed the capability ids
  skills.open_connections_hub / skills.connect_google → workflows.* in
  catalog_data; update the expected ids in catalog_tests to match.
- run_workflow spawn backstop: account a spawn against the process-lifetime
  cap only AFTER it actually starts (move account_spawn into the Ok arm).
  Previously unknown-workflow / bad-input rejections burned the budget, so
  in a long-lived process (the coverage test binary) rejected calls
  accumulated past 500 and tripped the guard for legitimate runs.

Co-Authored-By: Claude <noreply@anthropic.com>
…r gate

The runner consolidation + rename added FE lines the Coverage Gate flagged
(<80% on the diff). Add targeted Vitest coverage:
- skillsApi: updateSkill, cancelRun, createSkill optional-field branches.
- WorkflowsTab: card→runner navigation, listSkills error panel + retry,
  uninstall list-reconcile by id, create→runner.
- Intelligence: ?tab= deep link, dev-only tab guard, tab-pill switch.
- WorkflowRunnerBody: Stop on a RUNNING recent run (cancelRun), Edit modal
  from the locked header, "Run" on a saved schedule running directly with
  its snapshotted inputs (+ the parsed-input chips).
- CreateWorkflowForm: edit-mode prefill + submit via updateSkill.
- CreateSkillModal: Edit/Save labels in edit mode.

Co-Authored-By: Claude <noreply@anthropic.com>
…lias

- Replace undefined `selectTab` with `setActiveTab` in PillTabBar onChange
- Use existing `isVisibleTab` helper instead of inline array check (removes unused-var error)
- Add `alias = "skills"` on CapabilityCategory::Workflows serde so persisted JSON with the old category deserializes correctly
The Intelligence page tests expect the default tab (when no ?tab= param
is present) to be 'tasks'. Update the fallback to match the intended
behavior from the skills→workflows migration.
The older __tests__/Intelligence.test.tsx expected 'memory' as the
default tab and mocked the removed AgentWorkflows module. Updated
to expect 'tasks' default and mock WorkflowsTab instead, aligning
with the skills→workflows migration.
@senamakel senamakel merged commit 95118d5 into tinyhumansai:main Jun 5, 2026
19 checks passed
YellowSnnowmann added a commit to YellowSnnowmann/openhuman that referenced this pull request Jun 5, 2026
…erge

CreateSkillModal and InstallSkillDialog were removed from Skills.tsx
by upstream refactor (tinyhumansai#3324). addToast was also removed upstream.
MeetingBotsCard.onToast is optional so drop the prop.
YellowSnnowmann added a commit to YellowSnnowmann/openhuman that referenced this pull request Jun 5, 2026
…ev-only

- Re-wire onToast={addToast} to MeetingBotsCard in Skills.tsx.
  The prop was dropped after an upstream merge (tinyhumansai#3324); since onToast
  is optional, errors and join-success notices were silently discarded.
  Adds ToastContainer + local toast state following the same pattern as
  Intelligence.tsx.

- Guard ngrok-skip-browser-warning behind IS_DEV in apiClient.ts and
  backendHealth.ts. The header was added unconditionally to bypass ngrok
  interstitials during local tunnel testing but leaked into all production
  requests. It is now emitted only when import.meta.env.DEV is true.

Addresses review comments from M3gA-Mind (tinyhumansai#3363).
sanil-23 added a commit to sanil-23/openhuman that referenced this pull request Jun 5, 2026
…ly cores + spawn-cap regression test

Batch C of the skills→workflows internal migration (tinyhumansai#3324 follow-up).

C1 (triggered-workflow boot gap): register_triggered_workflow_subscriber
previously ran only inside start_channels, which bootstrap_core_runtime
skips for web-chat-only desktop installs (no messaging integration) and
when OPENHUMAN_DISABLE_CHANNEL_LISTENERS=1 — so any workflow declaring
'triggers:' was silently ignored on those cores. Extracted an idempotent
bus::ensure_triggered_workflow_subscriber(workspace) helper backed by a
single process-global OnceLock, and call it from BOTH start_channels and
bootstrap_core_runtime. The shared OnceLock guarantees exactly one
registration regardless of which path runs first.

  Caveat: the subscriber currently only *matches* triggers and logs — the
  activation handoff to the integration layer is still pending
  (TriggeredSkillSubscriber::handle). This enables matching on
  web-chat-only cores, not yet activation.

C2 (spawn-cap backstop): the functional guard already landed in tinyhumansai#3324
(account_spawn runs only in the Ok arm, after spawn_workflow_run_background
succeeds; unknown ids are rejected synchronously by registry::get_workflow
before accounting). This adds the missing regression test
(unknown_workflow_id_does_not_burn_a_spawn_slot) + a #[cfg(test)]
guard::total_spawns() reader, asserting a rejected spawn leaves the
process-lifetime backstop counter unchanged.
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.

2 participants