Skip to content

feat(flows): tinyflows Phases 3–8 — editable canvas, authoring/templates, workflow-builder agent, engine 0.5.0, Skills rename - #4570

Merged
senamakel merged 44 commits into
tinyhumansai:mainfrom
senamakel:feat/tinyflows-phase-3plus
Jul 5, 2026
Merged

senamakel merged 44 commits into
tinyhumansai:mainfrom
senamakel:feat/tinyflows-phase-3plus

Conversation

@senamakel

@senamakel senamakel commented Jul 5, 2026 •

Copy link
Copy Markdown
Member

Summary

Phases 3–8 of docs/plans/tinyflows-integration/README.md, on one branch. Turns the tinyflows viewer into a full n8n-class visual builder, adds a prompt-first authoring agent, extends the engine, and renames the misnamed legacy Skills backend.

  • Phase 3 — editable canvas: flips the read-only canvas to editable behind an editable prop (drag/connect with port-aware validation, delete, 12-kind node palette), a per-kind node config drawer (+ raw-JSON escape hatch + connection picker), inline validation UX via the existing flows_validate, explicit-Save draft/dirty state with an unsaved-changes guard, and a live run overlay driven by the flow:run_progress socket event (closes Phase 1's deferred frontend-socket follow-up).
  • Phase 4 — authoring/interop: a "New workflow" chooser (scratch / template / describe), 6 curated bundled WorkflowGraph templates + gallery, workflow export + host-validated import with a best-effort n8n importer (flows_import RPC), and "Open in canvas" on the chat proposal card.
  • Phase 5 — workflow-builder agent: a narrow-scoped workflow-builder agent definition + specialized prompt, a 7-tool belt (revise_workflow, read-only list_flows/get_flow/get_flow_run, list_flow_connections, search_tool_catalog, sandboxed dry_run_workflow), a FlowsPage prompt bar, a canvas copilot panel with an accept/reject draft diff overlay, and "Fix with agent" from failed runs. The agent never persists — proposals only.
  • Phase 6 — polish: flows_duplicate (disabled/unbound copy), run-history retention/pruning (never prunes parked/running), a run-inspector per-item data browser (table⟷JSON + paired_item), and a WDIO create→run→inspect E2E spec.
  • Phase 7 — engine (vendor/tinyflows 0.3.2→0.5.0): sub-workflow by workflow_id via a host-injected WorkflowResolver (plan item 4b), output_parser schema validation + one-shot LLM auto-fix, a cooperative CancellationToken in engine::run, and agent-node tool + output_parser sub-ports (partial). Docs truth-up.
  • Phase 8 — decommission (course-corrected): the "legacy workflows:: domain" turned out to be the live Skills backend (its README literally starts with "# Skills"), not dead code. Instead of deleting it (which would break Skills), this renames it: workflows:: module → skills::, the 11 workflows_* RPCs → skills_*, and the frontend workflowsApi → skillsApi. Storage schema unchanged (no data migration/loss). Frees the "Workflows" name for tinyflows.

Problem

After Phases 0–2, tinyflows had a live backend but a read-only UI, no authoring entry points, no prompt-first builder, an engine missing sub-workflow-by-reference / output-parser validation / cancellation, and a confusingly-named legacy domain sharing the "workflows" branding.

Solution

  • Frontend consumes the RPCs already shipped in Phases 1–2 (flows_validate, flows_list_connections, flow:run_progress) rather than adding backend surface; the editable canvas reuses the already-written xyflowToWorkflowGraph as the save path.
  • The builder agent routes via the orchestrator's build_workflow delegation edge (the runtime has no UI affordance to target a named agent); ChatRuntimeProvider now parses proposals from subagent tool results too.
  • Engine changes are additive/back-compat: existing entry points delegate through the new capability/token; sub_workflow still inlines a child graph and now also resolves by id via a host WorkflowResolver.
  • Phase 8 is a pure rename — RPC method strings + Rust symbols + frontend call sites — with storage untouched, verified by keeping the Skills test suites green.

Submission Checklist

  • Tests added or updated (happy path + failure/edge) — per-phase: flows Rust unit (142+), engine (260), frontend Vitest (1362 in the final skills/flows/intelligence run), JSON-RPC E2E for flows_import, WDIO create→run→inspect spec. Failure paths covered (invalid graph blocks Save; unknown cred fails closed; import placeholder nodes; cancellation).
  • Diff coverage ≥ 80% — new code carries co-located unit/E2E tests; the authoritative diff-cover gate runs in CI's coverage jobs.
  • Coverage matrix updated — N/A: no matrix row required (Coverage Matrix Sync passes as-is); new flows_import/flows_duplicate/skills_* rows can be added if reviewers want them.
  • All affected feature IDs listed under ## Related.
  • No new external network dependencies — mock backend used for E2E.
  • Manual smoke checklist — this DOES touch release-cut UI (the Flows builder + Skills rename); reviewers should smoke the /flows canvas create→run and the /settings/automations Skills surfaces.
  • Linked issue — N/A: tracked by the in-repo plan docs/plans/tinyflows-integration/README.md (Phases 3–8).

Impact

  • Runtime/platform: desktop core (Rust) + React UI + the vendor/tinyflows engine submodule (0.3.2→0.5.0). Requires the submodule branch feat/sub-workflow-by-id on tinyhumansai/tinyflows (pushed).
  • Skills rename (Phase 8) — breaking wire change, coordinated: openhuman.workflows_* → openhuman.skills_*. Backend + frontend renamed together in this PR. Storage schema unchanged — no user data migration, no data loss. The skills_list result still returns its array under the workflows JSON key (unchanged wire field). about_app category serde strings still emit "workflows" (with alias="skills") — deliberately deferred.
  • Security: builder agent is external_effect=false (proposals only, never persists/enables); dry_run_workflow runs against tinyflows mock caps (no real side effects) and is autonomy-tier gated + labeled sandbox.
  • Migration/compat: engine changes are additive (default paths unchanged). sub_workflow gains an optional workflow_id.

Related

  • Closes: N/A (plan-tracked, no GitHub issue)
  • Feature area: flows:: / openhuman.flows_* (tinyflows), skills:: / openhuman.skills_* (renamed), vendor/tinyflows engine
  • Follow-up PR(s)/TODOs:
    • Partial execution / run-from-node (deferred — needs engine node-state seeding; plan says scope with maintainers).
    • Agent chat_model/memory sub-ports + multi-turn tool loop (Phase 7.1 remainder).
    • Backend RPC duplication surfaced by the rename: skills_{run,cancel,read_run_log,recent_runs,uninstall} coexist with the FE-used skill_runtime_*/skill_registry_uninstall — decide which survives.
    • about_app category serde flip "workflows"→"skills" (coordinated FE/BE).
    • Tag the vendor/tinyflows engine work as a release (0.5.0) and re-vendor to a tag.
    • Full route-level unsaved-changes interception (needs a react-router data router; HashRouter limits useBlocker).

AI Authored PR Metadata

Linear Issue

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

Commit & Branch

  • Branch: feat/tinyflows-phase-3plus
  • Commit SHA: see branch head (32 commits)

Validation Run

  • pnpm --filter openhuman-app format:check — prettier applied to touched files per phase
  • pnpm typecheck — pass (0 errors, full-batch final run)
  • Focused tests: flows Rust cargo test --lib openhuman::flows:: (142); engine cargo test in vendor/tinyflows (260); frontend Vitest (1362 final run)
  • Rust fmt/check: GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml and app/src-tauri/Cargo.toml → both 0 errors
  • i18n: pnpm i18n:check parity green across 14 locales

Validation Blocked

  • command: full WDIO E2E execution
  • error: requires built app + driver (runs in CI Full)
  • impact: the new flows.spec.ts is authored + typechecked; validated in CI's desktop E2E matrix

Behavior Changes

  • Intended: tinyflows is now a full editable/prompt-first visual builder; the misnamed Skills RPCs are renamed skills_*.
  • User-visible: the /flows canvas is editable with live run animation, a prompt bar + copilot, templates, import/export; Skills UI unchanged in behavior (only its RPC names changed under the hood).

Parity Contract

  • Legacy behavior preserved: read-only canvas intact (editable defaults false); engine default paths unchanged (new caps/token are additive); Skills functionality identical post-rename (storage untouched).
  • Guard/fallback/dispatch parity: all RPCs via the controller registry (all.rs), not dispatch.rs; 2s run poller retained as socket fallback.

Duplicate / Superseded PR Handling

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

Summary by CodeRabbit

  • New Features

    • Added a workflow prompt bar and copilot panel for creating, revising, and repairing workflows.
    • Introduced an editable flow canvas with node palette, node configuration drawer, validation, save/discard, and run controls.
    • Added a template gallery, draft-canvas flow, plus export/import for flows (including “Export” and JSON export downloads).
    • Expanded run inspection with table/JSON viewing, binary placeholders, source reveal, and a “Fix with agent” action; proposals can open in the unsaved canvas.
  • Bug Fixes

    • Improved live run status updates, and made validation/error messaging more reliable.
    • Added safer draft navigation/unsaved-change handling.

senamakel added 30 commits July 5, 2026 02:52
Move NODE_KIND_META / DEFAULT_NODE_META / COLOR_CLASSES / handleOffsets
out of FlowNodeComponent into a dependency-free lib/flows/nodeKindMeta.ts
so the editable canvas's node palette can pull the same emoji/accent from
one source of truth. Also exports NODE_KINDS (ordered kind list) and a
nodeKindMeta() lookup helper. FlowNodeComponent now imports from it —
render output is unchanged.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Add two pure helpers to graphAdapter for the editable canvas:
- isValidFlowConnection(connection, nodes, edges): reuses the canvas's
  derived input/output ports to accept only valid connections; rejects
  missing endpoints, self-loops, unknown nodes/handles, and duplicate
  edges. Shared by <ReactFlow isValidConnection> (live drag feedback)
  and onConnect (the commit).
- createFlowNode(kind, position, id, name?): builds a fresh xyflow node
  with a single default main input/output handle, empty config/ports —
  round-trips cleanly through xyflowToWorkflowGraph.

Unit tests cover both plus a palette-built graph round-trip.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…client

Wire the three Phase 1-2 backend controllers into the flows RPC client,
following the existing callCoreRpc + unwrapCliEnvelope pattern:
- updateFlow(id, {name?, graph?, requireApproval?}) — the canvas Save
  path (flows_update; server re-validates before persisting).
- validateFlow(graph) — pure, cheap flows_validate for inline validation.
- listFlowConnections() — secret-free FlowConnection[] for the node-config
  credential picker.

Adds FlowValidation / FlowConnection / FlowUpdate wire types.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Add an `editable` prop to FlowCanvas (default false — the read-only
viewer at /flows/:id is untouched). When editable, FlowCanvas delegates
to the new EditableFlowCanvas, which lifts nodes/edges into controlled
state and wires:

- drag/move — nodesDraggable; onNodesChange persists positions.
- connect — port-aware onConnect via isValidFlowConnection; rejects
  invalid connections (self-loop, unknown handle, duplicate) and reports
  them through onInvalidConnection; isValidConnection paints live drag
  feedback.
- delete — Backspace/Delete plus an explicit "Delete selected" toolbar
  button; deleting a node also drops its incident edges.
- add — NodePalette lists all 12 node kinds (shared emoji/accent) and
  inserts by click (cascade position) or drag-drop (under the cursor).
- save — a "Save" button serializes the live canvas to a WorkflowGraph
  via xyflowToWorkflowGraph and hands it to onSave (the flows_update
  call + dirty-guard land in 3d).

FlowCanvasPage drops the now-removed `readonly` prop (default is
read-only). New i18n keys flows.palette.* / flows.editor.* added to
en + all 13 locales. Behavior tests drive the palette/save click paths.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
… 3b)

New flows.nodeConfig.* namespace for the node config side-panel: drawer
chrome, shared field affordances (expression badge, key/value map, raw-JSON
editor), the credential picker, and per-kind form labels (trigger,
http_request, agent, tool_call, condition, switch, transform, code).

Real translations added to all 13 non-English locales (ar, bn, de, es, fr,
hi, id, it, ko, pl, pt, ru, zh-CN); i18n:check key-parity passes and the
additions introduce zero new i18n:english:check flags.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…tch (Phase 3b)

Right-hand drawer that opens on single-node select in the editable canvas:

- Shared, controlled field primitives (nodeConfigFields): text/textarea/
  select, an =-expression field with a monospace input + "Expression"
  affordance, a key→value map (headers / transform set), a JSON editor that
  holds invalid intermediate buffers, and a secret-free credential picker fed
  by flows_list_connections (shows display/kind only).
- Per-kind forms (nodeConfigForms) for high-traffic kinds — trigger
  (schedule/webhook/app_event), http_request (method/url/headers/body +
  http-only credential), agent (prompt/model + credential), tool_call
  (slug/args + credential), condition (field), switch (expression/field),
  transform (set map), code (language toggle + source). Field keys mirror what
  vendor/tinyflows reads at runtime.
- NodeConfigDrawer shell: kind badge + inline name edit, the per-kind form,
  a raw-JSON escape hatch for kinds without a form (plus an opt-in
  Edit-as-JSON toggle for every kind), Escape/close semantics. Every edit is
  a controlled onChange(nodeId, patch) — no local config mirror that drifts.

Co-located Vitest covers http_request, transform, and trigger forms, the
kind-filtered credential picker, and the drawer (name/form edits, raw-JSON
toggle, raw default for form-less kinds, close).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Wire the NodeConfigDrawer into EditableFlowCanvas: onSelectionChange opens the
drawer for an unambiguous single-node selection (any edge or 0/2+ nodes closes
it), edits apply to live node state via a controlled updateNode(nodeId, patch),
and closing deselects so re-clicking the same node reopens. Credential refs are
loaded once via flows_list_connections, guarded so a non-Tauri/RPC failure just
yields the picker's empty state.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Add the pieces the editable Workflow Canvas needs to surface flows_validate
results inline, ahead of wiring them into the canvas:

- lib/flows/flowValidation.ts: erroredNodeIds() maps graph-level validation
  error strings back to the node ids they name (whole-token match, hyphen-safe)
  so the canvas can ring the offending node(s). Unit-tested.
- useFlowValidation.ts: debounced + on-demand validation of the live draft
  against openhuman.flows_validate; RPC failures fail open (advisory only).
- FlowValidationBanner.tsx: inline errors (coral) / warnings (amber) surface,
  plus a distinct save-failure row.
- flowCanvasStyles.css: .flow-node-error ring for a node named by a hard error.
- i18n: flows.editor.{validate,validating,discard,unsaved,saving,saveBlocked,
  errorsTitle,warningsTitle,saveFailedTitle,leave*} across all 14 locales.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…e 3c/3d)

Turn the editable Workflow Canvas into a real builder host:

Validation UX (3c):
- EditableFlowCanvas runs useFlowValidation on the live draft (debounced +
  a manual "Validate" button), renders FlowValidationBanner (errors block Save,
  warnings don't), and rings error-named nodes via displayNodes className.
- Save is gated on hard errors (button disabled + belt-and-braces guard).

Draft/dirty state (3d):
- Dirty is derived by comparing the serialized live graph to a saved baseline;
  reported up via onDirtyChange. A "Discard changes" button resets to baseline;
  an "Unsaved changes" badge shows while dirty.
- Save (no autosave — a live flow would fire schedules) awaits onSave and only
  advances the baseline on success; a failure surfaces inline and stays dirty.
- FlowCanvasPage now hosts the editable canvas: onSave -> flows_update(id,
  { graph }); an unsaved-changes guard warns on tab close (beforeunload) and
  intercepts the Back button with a confirm dialog. (HashRouter has no data
  router, so full useBlocker route interception isn't available.)

Tests: invalid graph blocks Save + rings the node + shows the banner; warnings
allow Save; dirty gates Save/Discard, Discard resets, Save clears dirty; a Save
failure surfaces inline; page persists via flows_update and prompts before
leaving while dirty.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…Progress

Consume the core's FlowRunProgress feed on the frontend (closes Phase 1's
deferred follow-up). New useFlowRunProgress(runId) hook subscribes to the
socket `flow:run_progress`/`flow_run_progress` aliases and yields a
node_id -> live status map, filtered to the watched run and torn down on
unmount / run change (mirrors useTinyplaceStream). EditableFlowCanvas composes
the status into displayNodes so nodes ring running (pulsing)/success/error —
n8n's signature interaction. The 2s useFlowRunPoller stays as the durable
fallback; the socket just makes it live.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…pector

Add a Run button to FlowCanvasPage that starts the persisted flow and hands
its thread_id to the canvas as activeRunId, so the live overlay is reachable
(runs the saved version, not the dirty draft — matching the explicit-save
model). Reflect the same live per-node status in FlowRunInspectorDrawer by
colouring each step's dot from useFlowRunProgress (ocean/sage/coral), keeping
the poller's durable step list as the source of truth. Adds flows.editor.run /
running / runFailed i18n keys with full translations across all locales.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Add an "Open in canvas" action to the chat WorkflowProposalCard so a
user can review/edit an agent-proposed WorkflowGraph on the editable
canvas BEFORE the single persistence gate.

Critical invariant: opening a draft never persists or enables a flow.
The proposed graph rides in router `location.state` (ephemeral — dropped
on reload, exactly what an unsaved draft should be) to a new
`/flows/draft` route hosting the editable canvas. No flows_create /
flows_update fires on open; the canvas's own Save button stays the one
persistence gate (for a draft it calls flows_create, then replaces into
the new flow's `/flows/:id` canvas). Drafts aren't runnable until saved.

- lib/flows/canvasDraft.ts: shared route constant + draft-state type/guard
- WorkflowProposalCard: "Open in canvas" button navigates with the draft;
  proposal left intact in the thread (not dismissed)
- FlowCanvasPage: FlowEditor generalized to saved-or-draft; new
  FlowCanvasDraftPage reads the draft from router state (empty state when
  absent); AppRoutes wires `/flows/draft` before `/flows/:id`
- i18n: chat.flowProposal.openInCanvas + flows.canvas.draftMissing across
  all 14 locales
- Tests: open-in-canvas navigates without persisting; draft renders
  without fetch, saves via flows_create (never flows_update), empty state

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…data)

Add six structurally-valid tinyflows templates under app/src/lib/flows/
templates/ as static JSON with a typed index (no dynamic imports), plus a
validity test asserting each graph has exactly one trigger, unique node ids,
and edges that reference existing nodes. Display strings are i18n'd via
flows.templates.* / flows.chooser.* keys added to en.ts and all 13 locales.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Replace FlowsPage's /chat TODO with a chooser modal (NewWorkflowModal): start
from scratch (creates a blank flow with a single manual trigger via
flows_create, then opens the canvas), from a template (FlowTemplateGallery
over the bundled templates), or describe it (interim Chat hand-off, TODO
phase-5). The empty state also surfaces the template gallery inline. Shared
create-and-open logic lives in the useCreateFlow hook; the blank starter graph
in lib/flows/newFlow.ts. Adds behavior tests for all three chooser paths, the
blank-graph shape, and the empty-state gallery, and updates the FlowsPage tests
for the new chooser behavior.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Add `openhuman.flows_import(graph, {format})`: parse a native tinyflows
WorkflowGraph or an n8n workflow export, migrate + validate it host-side,
and return the normalized graph plus non-fatal warnings WITHOUT persisting
(the user Saves via the existing flows_create gate).

- New `flows/n8n_import.rs`: maps the overlapping n8n vocabulary (if→condition,
  switch→switch, merge→merge, splitOut→split_out, httpRequest→http_request,
  code/function→code, scheduleTrigger/cron→trigger(schedule), webhook→
  trigger(webhook), manualTrigger→trigger(manual)). Unmapped node types land as
  annotated placeholder `transform` nodes (original type+params preserved), never
  a failed import. `={{ $json.path }}` expressions translated to `=`-jq where
  trivial, else kept raw + warned. Connections/positions preserved; triggers
  reconciled to exactly one (synthesize manual / demote extras) so validate passes.
- `FlowImport { graph, warnings }` type; auto-detect native vs n8n by shape.
- Wired via schemas.rs + all.rs registry (no dispatch.rs). Unit tests for the
  n8n mapping (IF→condition, unmapped→placeholder, expression translation, etc.).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…ort UI

- Export: a per-row "Export" action downloads a flow's WorkflowGraph as
  `<name>.flow.json` (pure client-side serialize; new lib/flows/exportFlow.ts).
- Import: a header "Import" button opens a file picker → flowsApi.importFlow →
  runs the JSON through `openhuman.flows_import` (host migrate + validate +
  best-effort n8n mapping) and opens the normalized graph on the editable canvas
  as an UNSAVED draft via the existing /flows/draft route. Nothing persists until
  the user Saves. Auto-detects native vs n8n.
- Import warnings ride in FlowCanvasDraftState.importWarnings and surface as
  dismissible toasts over the draft canvas so n8n approximations aren't lost.
- i18n keys added to en.ts + all 13 locales (real translations).
- Vitest: exportFlow (slug/serialize/download), flowsApi.importFlow, FlowListRow
  export action, FlowsPage import/export flows.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Exercises `openhuman.flows_import` end-to-end: a native tinyflows graph
imports warning-free; an auto-detected n8n export maps IF→condition and an
unmapped Airtable node to an annotated placeholder with a warning; and neither
call persists a Flow (flows_list stays empty).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…run)

Adds the deliberately narrow agent tool belt for authoring tinyflows
graphs in chat (src/openhuman/flows/builder_tools.rs), registered in
tools/ops.rs:

- revise_workflow — validate a revised draft -> proposal (never persists;
  same contract as propose_workflow, reuses build_summary)
- list_flows / get_flow / get_flow_run / list_flow_connections — read-only
  (PermissionLevel::None), delegate to flows::ops
- search_tool_catalog — grounds tool_call slugs in the real curated Composio
  catalog (agent_ready_toolkits + catalog_for_toolkit)
- dry_run_workflow — runs a DRAFT against tinyflows MOCK capabilities (no
  real side effects), autonomy-tier gated (read-only refuses), output
  labeled sandbox; enables the tinyflows `mock` feature

16 unit tests: each tool's happy path, read-only tools return None, revise
never persists, dry_run tier-gated + sandbox-labeled.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Adds the workflow_builder built-in agent (Worker tier, burst hint) that
turns a natural-language automation request into a validated tinyflows
WorkflowGraph PROPOSAL — it never persists or enables a flow.

- agent_registry/agents/workflow_builder/{agent.toml,prompt.rs,prompt.md,mod.rs}
  ToolScope::Named is EXACTLY the Phase 5b propose-or-read belt (no shell,
  no file writes, no channel sends, no flows_create/update/set_enabled);
  overridable by user TOML via the existing definition_loader path.
- prompt.md teaches the 12 node kinds, =/jq expression semantics, port/edge
  rules, which trigger kinds fire live (manual/schedule/app_event) vs warn,
  per-node on_error/retry/requires_approval, and the propose-never-persist
  invariant.
- Registered in agents/mod.rs + loader.rs BUILTINS; reachable by delegation
  as `build_workflow` from the orchestrator's subagents allowlist.
- Registry tests: narrow scope + Worker leaf + no persistence tools +
  orchestrator delegation edge; prompt teaches the invariant; id present in
  the builtin_definitions expected-ids test.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
… routing

Add a "Describe a workflow…" prompt bar to the Flows page (hero on the
empty state, compact otherwise). Submitting spawns a workflow-builder turn
in a dedicated thread via a new useWorkflowBuilderChat hook and renders the
returned proposal inline with the existing WorkflowProposalCard.

Turns route to the workflow_builder specialist via the orchestrator's
build_workflow delegation (there is no direct entry-agent selection in the
runtime), so the propose_workflow/revise_workflow result is produced inside
the delegated worker. ChatRuntimeProvider now parses that proposal from
onSubagentToolResult too (not just onToolResult), keyed on the parent thread,
so the card surfaces regardless of which agent emitted it. Still validate-only:
Save & enable stays the sole persistence gate.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Dock a workflow-builder copilot on the editable canvas (/flows/:id and
drafts). Each turn injects the current draft graph as context and calls
revise_workflow; the returned proposal enters a diff-preview overlay —
added nodes ringed sage, removed nodes ghosted — that the user Accepts
(applies to the local Phase 3d draft) or Rejects (reverts). A revise loop
("add a Slack notification on failure", "weekdays only").

EditableFlowCanvas gains onGraphChange + added/removed id sets + saveDisabled;
FlowEditor holds the draft/preview state and re-seeds the canvas per accept/
reject. Nothing persists — Save stays the only gate and is disabled while a
proposal is under review.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
A failed run's inspector now surfaces a "Fix with agent" action that hands
the run context (flow id, run id, error, failing node ids) up through
FlowRunsDrawer to the Flows page, which navigates to the flow's canvas with
a copilot repair seed in location.state. The copilot opens preloaded and
auto-sends a repair turn (get_flow_run + diagnose + propose). Propose-only:
no persistence.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…etention/pruning

Add openhuman.flows_duplicate(id): creates an independent copy of a saved
flow's graph under a new id, name suffixed " (copy)", enabled=false so it is
NOT schedule/trigger-bound and can never immediately fire. require_approval
carries over; run history resets.

Add per-flow run retention: MAX_FLOW_RUNS_PER_FLOW cap pruned on every new-run
insert (atomic in the same connection) and via a manual openhuman.flows_prune_runs
sweep. Prune deletes only terminal runs (completed/failed/cancelled) outside the
newest-N window; a running or pending_approval run is never pruned, so a parked
run can't be removed out from under a flows_resume.

Wired both controllers via schemas.rs + the flows registry (all.rs picks them up
through the aggregation fns). Verbose [flows] logging throughout.

Tests: duplicate produces a disabled, unbound copy with a new id and identical
graph; pruning keeps the N newest and spares pending_approval/running; insert
auto-prunes past the cap; store round-trips.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…ut pairing

Add the n8n-signature table⟷JSON toggle to the run inspector's per-step
output, plus a paired_item-driven "Source" affordance that reveals the input
item a given output item derived from.

- lib/flows/runItems.ts: forgiving normalizer turning a step's opaque `output`
  into `FlowRunItem[]` ({ json, binary, pairedIndex }); union-column derivation
  and cell/JSON formatters. Never throws on messy real-world payloads.
- components/flows/RunItemDataBrowser.tsx: extracted data browser. Table view
  (one row per item, union columns, truncating cells, horizontal scroll), JSON
  view (pretty-printed payloads), binary placeholder chips (name/MIME, never
  inlined), and per-row source-input reveal when paired_item resolves.
- FlowRunInspectorDrawer: StepRow now renders the data browser inside the
  Output disclosure; passes the upstream step's items as pairing input.
- i18n: 10 new flowRuns.inspector.* keys across en + all 13 locales.
- Tests: normalizer unit tests, data-browser component tests (table rows/union
  columns, JSON toggle, binary chip, pairing present/absent), updated drawer
  test for the new table default.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Add app/test/e2e/specs/flows.spec.ts driving the Workflows flow end-to-end
through real UI clicks: open /flows, create from scratch via the New workflow
chooser, run on the editable canvas, then open the run inspector and assert a
terminal (Completed) status with at least one step. Uses existing data-testids
on the flows components and the shared element/reset/nav helpers; no component
changes needed. Trigger-only graph runs deterministically in the local
tinyflows engine under the mock backend.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
… (Phase 7A)

Bump vendored tinyflows to 0.4.0 (adds the WorkflowResolver capability + the
sub_workflow-by-id engine path) and wire the host side:

- flows::ops::load_flow_graph — sync id → saved-flow WorkflowGraph lookup.
- OpenHumanWorkflowResolver (caps.rs) implements tinyflows::caps::WorkflowResolver
  over the flows store; unknown id → capability error naming the id. Wired into
  build_capabilities so a sub_workflow node carrying `workflow_id` loads and runs
  the referenced saved flow's graph. Verbose [flows] logging throughout.
- Cargo.toml dep requirement 0.3 → 0.4 so the [patch] path override still applies.

Host tests: resolver loads a saved flow's graph by id; unknown id errors.
Gates: GGML_NATIVE=OFF cargo check green; cargo test --lib openhuman::flows:: (142)
and the resolver tests green.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…ion, cancellation token, agent sub-ports)

Advance the vendor/tinyflows submodule gitlink to the Phase 7B engine commit and
bump the dep requirement (0.4 -> 0.5). Host cancel path (task-abort + run_registry)
left as-is; the engine now additionally exposes a cooperative CancellationToken
(run_cancellable / resume_cancellable) available for future belt-and-suspenders
wiring. No host source changes required — the new engine API is additive and the
existing run/resume entry points are unchanged.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
The `src/openhuman/workflows/` domain is the Skills discovery/parse/
install/run backend (its README literally starts with "# Skills"). Rename
it to `skills::` to free the "Workflows" name for tinyflows.

- git mv src/openhuman/workflows -> src/openhuman/skills
- src/openhuman/mod.rs: `pub mod workflows;` -> `pub mod skills;`
- all_workflows_{registered_controllers,controller_schemas}
  -> all_skills_* (+ call sites in src/core/all.rs)
- module path crate::openhuman::workflows:: -> crate::openhuman::skills::
- RPC methods renamed via `namespace: "workflows"` -> `"skills"`:
  workflows_{create,list,update,describe,install_from_url,read_resource,
  uninstall,run,cancel,read_run_log,recent_runs} -> skills_*
- handler fns handle_workflows_* -> handle_skills_*, dispatcher
  workflows_schemas -> skills_schemas, namespace_description arm,
  doc comments
- On-disk `.openhuman/workflows/` dirs, `Workflow`/`WorkflowSummary`
  types, and the `workflows` wire output field are UNCHANGED (no data
  migration). skill_registry/skill_runtime/flows/workflow_run untouched.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Follow-up to the workflows->skills domain rename: update JSON-RPC E2E
callers (openhuman.workflows_{create,list,describe,uninstall} ->
skills_*) and openhuman_core::openhuman::workflows:: module paths in
json_rpc_e2e.rs, agent_round26_raw_coverage_e2e.rs, and
x402_twit_sh_live.rs. The `workflows` list-result data field is left
unchanged (wire data, not an RPC method).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
The backend `workflows::` domain was renamed to `skills::`, renaming its
JSON-RPC namespace prefix workflows_* -> skills_*. Repoint every frontend
call site (workflows_{list,read_resource,create,update,install_from_url,
describe}) plus the corresponding Vitest mock assertions and doc comments.

The skill_runtime_* and skill_registry_uninstall calls are unchanged — those
are separate, still-registered backend domains, not part of the rename.

Wire-field keys left intact: skills_list still returns its array under the
`workflows` JSON key, and create/update still return `workflow`.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Pre-push hook's format:check surfaced formatting drift the per-file
agent checks missed (long-chain/match reflow in Rust, prettier
normalization across flows UI + i18n locale files). Formatting-only;
no logic change. Includes a benign transitive Cargo.lock re-resolution
(windows-sys 0.60→0.61). tinyflows pin unchanged (0.5.0).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
@senamakel
senamakel requested a review from a team July 5, 2026 17:37
@coderabbitai

coderabbitai Bot commented Jul 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f052315f-0f11-46ed-8607-e8fa3ec977e4

📥 Commits

Reviewing files that changed from the base of the PR and between 627f33e and 9b623ae.

📒 Files selected for processing (16)
  • app/src/components/chat/WorkflowProposalCard.test.tsx
  • app/src/components/chat/WorkflowProposalCard.tsx
  • app/src/components/flows/RunItemDataBrowser.tsx
  • app/src/components/flows/canvas/EditableFlowCanvas.tsx
  • app/src/components/flows/canvas/FlowCanvas.tsx
  • app/src/components/flows/canvas/__tests__/EditableFlowCanvas.validation.test.tsx
  • app/src/components/flows/canvas/useFlowValidation.ts
  • app/src/hooks/useFlowRunProgress.ts
  • app/src/hooks/useWorkflowBuilderChat.ts
  • app/src/lib/flows/graphAdapter.test.ts
  • app/src/lib/flows/graphAdapter.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/pages/FlowCanvasPage.tsx
  • app/src/services/__tests__/rpcMethods.test.ts
  • src/openhuman/flows/n8n_import.rs
  • src/openhuman/skills/schemas/handlers.rs
✅ Files skipped from review due to trivial changes (1)
  • app/src/lib/i18n/zh-CN.ts
🚧 Files skipped from review as they are similar to previous changes (13)
  • app/src/components/chat/WorkflowProposalCard.test.tsx
  • app/src/components/chat/WorkflowProposalCard.tsx
  • app/src/components/flows/canvas/useFlowValidation.ts
  • app/src/lib/flows/graphAdapter.test.ts
  • app/src/lib/flows/graphAdapter.ts
  • app/src/hooks/useWorkflowBuilderChat.ts
  • app/src/hooks/useFlowRunProgress.ts
  • app/src/components/flows/canvas/FlowCanvas.tsx
  • app/src/pages/FlowCanvasPage.tsx
  • app/src/components/flows/RunItemDataBrowser.tsx
  • app/src/components/flows/canvas/EditableFlowCanvas.tsx
  • src/openhuman/flows/n8n_import.rs
  • src/openhuman/skills/schemas/handlers.rs

📝 Walkthrough

Walkthrough

This PR renames the workflows subsystem to skills across Rust and frontend APIs, and adds editable flow canvas, template/gallery creation, import/export, copilot, live run repair, and the workflow_builder agent/tooling. It also updates translations and tests for the new flows.

Changes

Workflows→Skills Rename

Layer / File(s) Summary
Rust namespace and controller rename
src/openhuman/skills/*, src/openhuman/skill_registry/*, src/openhuman/skill_runtime/*, src/openhuman/agent/..., src/core/*, src/openhuman/tools/*
openhuman::workflows and workflows_* JSON-RPC methods are renamed to openhuman::skills/skills_* across module declarations, controllers/handlers/schemas, agent harness types, channels wiring, and tools/tests.
Frontend API and consumer rename
app/src/services/api/skillsApi.ts, app/src/components/skills/*, app/src/components/intelligence/WorkflowsTab.tsx, tests
workflowsApi is renamed to skillsApi, RPC method strings are updated to skills_*, and consuming components/tests are updated accordingly.
Rust rename tests
tests/*, src/openhuman/*tests.rs
Unit, integration, and e2e tests are updated to reference skills module paths and skills_* RPC names.

Workflow Canvas and Builder Feature

Layer / File(s) Summary
Flow backend APIs and persistence
src/openhuman/flows/*, app/src/services/api/flowsApi.ts
Adds flow import/duplicate/prune operations, n8n import mapping, flow retention, FlowImport, and flowsApi.updateFlow/validateFlow/importFlow/listFlowConnections.
Editable canvas core
app/src/components/flows/canvas/*, app/src/lib/flows/{graphAdapter,nodeKindMeta,flowValidation}.ts
Introduces the editable canvas, node palette, validation banner, node-kind metadata, connection validation, and live run-overlay styles/tests.
Node config drawer
app/src/components/flows/canvas/nodeConfig/*
Adds shared field primitives, per-kind node config forms, and the drawer that toggles between form and raw JSON editing.
New workflow creation and templates
app/src/components/flows/{NewWorkflowModal,FlowTemplateGallery,useCreateFlow}.tsx, app/src/lib/flows/{newFlow,templates/*,canvasDraft,exportFlow}.ts, app/src/pages/FlowsPage.tsx
Adds blank/template flow creation, chooser modal UI, JSON export/import with draft-canvas handoff, and flow list export wiring.
Workflow builder copilot
app/src/hooks/useWorkflowBuilderChat.ts, app/src/components/flows/{WorkflowCopilotPanel,WorkflowPromptBar,WorkflowProposalCard}.tsx, app/src/lib/flows/{graphDiff,workflowBuilderPrompt}.ts, app/src/providers/ChatRuntimeProvider.tsx
Adds a dedicated workflow-builder chat thread, diff-based proposal preview/accept/reject, and prompt-first authoring UI.
Run inspector and repair flow
app/src/hooks/useFlowRunProgress.ts, app/src/lib/flows/runItems.ts, app/src/components/flows/{FlowRunInspectorDrawer,FlowRunsDrawer,RunItemDataBrowser}.tsx, app/src/pages/FlowCanvasPage.tsx, app/src/AppRoutes.tsx
Adds live per-node run status, a normalized item browser, a “Fix with agent” handoff, and an editable canvas page with a draft route.
Workflow builder agent (Rust)
src/openhuman/agent_registry/agents/workflow_builder/*, src/openhuman/flows/builder_tools.rs, src/openhuman/tools/{mod,ops}.rs
Registers a sandboxed workflow_builder agent with validate-only/read-only tools and a mock-capability dry-run executor.
i18n translations
app/src/lib/i18n/*.ts
Adds translation keys for the new prompt bar, copilot, canvas editor, node config, chooser, templates, and run inspector across locales.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WorkflowCopilotPanel
  participant useWorkflowBuilderChat
  participant ChatRuntimeProvider
  participant EditableFlowCanvas
  User->>WorkflowCopilotPanel: type revise instruction, Send
  WorkflowCopilotPanel->>useWorkflowBuilderChat: send(buildRevisePrompt)
  useWorkflowBuilderChat->>ChatRuntimeProvider: chatSend to dedicated thread
  ChatRuntimeProvider-->>useWorkflowBuilderChat: WorkflowProposal (revise_workflow)
  useWorkflowBuilderChat-->>WorkflowCopilotPanel: proposal
  WorkflowCopilotPanel->>WorkflowCopilotPanel: diffGraphs(current, proposal)
  User->>WorkflowCopilotPanel: Accept
  WorkflowCopilotPanel->>EditableFlowCanvas: onAccept(proposal)
Loading
sequenceDiagram
  participant User
  participant FlowRunInspectorDrawer
  participant useFlowRunProgress
  participant FlowsPage
  participant FlowCanvasPage
  User->>FlowRunInspectorDrawer: open failed run
  FlowRunInspectorDrawer->>useFlowRunProgress: live node statuses
  User->>FlowRunInspectorDrawer: click "Fix with agent"
  FlowRunInspectorDrawer->>FlowsPage: onFixWithAgent(FlowRepairRequest)
  FlowsPage->>FlowCanvasPage: navigate with location.state.copilotRepair
  FlowCanvasPage->>FlowCanvasPage: seed WorkflowCopilotPanel repair turn
Loading

Possibly related issues

Possibly related PRs

Suggested labels: feature, rust-core, agent

Suggested reviewers: M3gA-Mind, graycyrus, sanil-23

Poem

A rabbit hopped through skills and flows,
Then planted canvases in rows.
It sniffed out drafts, then fixed the run,
With copilot chats and template fun.
🐇✨ The burrow glows!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR’s main themes: editable flows canvas, authoring/templates, workflow-builder agent, engine update, and skills rename.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 627f33e422

ℹ️ About Codex in GitHub

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

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

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

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

Comment on lines +176 to +178
setDraftGraph(proposal.graph as WorkflowGraph);
setPreview(null);
setCanvasVersion(v => v + 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve copilot proposals as unsaved edits

When a user accepts a copilot proposal and makes no additional manual edits, this remounts the canvas with the proposed graph as the new initial state. EditableFlowCanvas seeds its dirty baseline from those initial nodes/edges, so the accepted proposal is immediately considered clean: the Save button stays disabled and the navigation guard stands down, causing the accepted changes to be lost on back/reload instead of being persistable through the required Save gate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 74c94ea — EditableFlowCanvas now accepts an initialDirty prop that force-seeds the dirty flag at mount (cleared on a real Save/Discard). FlowCanvasPage computes it by diffing the canvas's current graph against a ref of the last-persisted graph (only advanced by a successful Save), so an accepted-but-unpersisted proposal stays flagged dirty across the canvasVersion remount — this generalizes to the reject/preview remounts too, not just accept. Added a regression test in EditableFlowCanvas.validation.test.tsx.

Comment thread app/src/lib/flows/graphAdapter.ts Outdated
Comment on lines +252 to +254
ports: [],
inputPorts: [DEFAULT_PORT],
outputPorts: [DEFAULT_PORT],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Seed branch nodes with runtime branch ports

When a user adds a condition node from the palette, it is created with no declared ports and only a main output handle, but conditions route via true/false ports in the flow contract/templates and the config drawer has no way to edit node ports. This makes newly-authored conditional flows impossible to wire correctly from the canvas: authors can only draw a main edge, which does not correspond to the runtime branch outputs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 74c94ea — createFlowNode now seeds condition nodes with declared true/false output ports at creation time (other kinds unaffected; switch's case ports stay config/edge-derived since they only materialize once wired). Added a regression test in graphAdapter.test.ts.

Comment thread src/openhuman/flows/n8n_import.rs Outdated
Comment on lines +270 to +271
if let Some(code) = cfg.remove(src) {
cfg.entry("code".to_string()).or_insert(code);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Import n8n code into the source field

For n8n code/function nodes, this stores the imported body under config.code, while the rest of the flow surface and execution contract use config.source for code nodes. Imported code nodes therefore open in the canvas with an empty Source field (and execute without the imported body unless the user manually rewrites it), so code-heavy n8n imports silently lose their executable content.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 0d92f23 — map_code now writes the translated n8n jsCode/functionCode/pythonCode body under source (the key the code node actually reads, per vendor/tinyflows/src/nodes/integration/code.rs) instead of code. Updated the existing unit test to assert the correct key.

@coderabbitai coderabbitai Bot added 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. labels Jul 5, 2026
senamakel added 3 commits July 5, 2026 10:59
…ig.code

The code/function node reads its body from config.source
(vendor/tinyflows/src/nodes/integration/code.rs), but map_code() was writing
the translated jsCode/functionCode/pythonCode body under a `code` key instead
— so imported code nodes opened in the canvas with an empty Source field and
silently lost their executable content until the author manually rewrote it
(addresses @chatgpt-codex-connector on src/openhuman/flows/n8n_import.rs:271).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
A palette-added condition node had no declared ports and only the default
main output handle, but tinyflows' condition node always routes runtime
output through true/false (vendor/tinyflows/src/nodes/control_flow/condition.rs)
and the node config drawer has no port editor — so a freshly-authored
conditional flow was impossible to wire correctly: only a single main edge
could ever be drawn, which doesn't correspond to the runtime branch outputs.
createFlowNode now seeds condition nodes with declared true/false ports
(other kinds are unaffected — e.g. switch's case ports stay config/edge
-derived, since they only materialize once wired, per effectiveOutputPorts'
existing doc comment)
(addresses @chatgpt-codex-connector on app/src/lib/flows/graphAdapter.ts:254).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
Accepting a copilot proposal (with no further manual edit) bumps
FlowCanvasPage's canvasVersion to remount EditableFlowCanvas with the
proposed graph as its new nodes/edges. That component seeds its dirty
baseline from whatever it's mounted with, so the accepted-but-unpersisted
proposal instantly read as clean: Save stayed disabled and the unsaved
-changes nav guard stood down, silently losing the accepted change on
back/reload instead of gating it behind the required Save.

EditableFlowCanvas now accepts an initialDirty prop that force-seeds the
dirty flag at mount (cleared on a real Save/Discard); FlowCanvasPage computes
it by diffing the canvas's current graph against a ref of the last-persisted
graph (only advanced by handleSave), so it survives any number of
accept/reject/preview remounts, not just the accept case
(addresses @chatgpt-codex-connector on app/src/pages/FlowCanvasPage.tsx:178).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
src/openhuman/skills/schemas/handlers.rs (1)

32-66: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale [workflows] log prefixes survived the handler rename.

handle_skills_list (lines 36, 56), handle_skills_cancel (line 182), and handle_skills_update (lines 264, 277) still emit "[workflows][rpc] ..." tracing labels, while every other handler in this file (create, read_resource, install_from_url, uninstall, describe, recent_runs, read_run_log) was consistently updated to "[skills][rpc] ...". This will make log-based debugging/searching inconsistent for the renamed RPC surface.

🏷️ Proposed fix for stale log prefixes
-        tracing::debug!(include_skills, "[workflows][rpc] list automations");
+        tracing::debug!(include_skills, "[skills][rpc] list automations");
@@
-            "[workflows][rpc] list result"
+            "[skills][rpc] list result"
@@
-        tracing::info!(run_id = %payload.run_id, cancelled, "[workflows][rpc] cancel");
+        tracing::info!(run_id = %payload.run_id, cancelled, "[skills][rpc] cancel");
@@
-            "[workflows][rpc] update"
+            "[skills][rpc] update"
@@
-                tracing::debug!(error = %err, "[workflows][rpc] update: rejected");
+                tracing::debug!(error = %err, "[skills][rpc] update: rejected");

Also applies to: 174-188, 255-282

🤖 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/skills/schemas/handlers.rs` around lines 32 - 66, The tracing
labels in the renamed skills RPC handlers still use the old “[workflows][rpc]”
prefix, making logs inconsistent with the rest of the file. Update the debug
calls in handle_skills_list, handle_skills_cancel, and handle_skills_update to
use the “[skills][rpc]” prefix everywhere those messages are emitted, matching
the other handlers in this module. Keep the existing log structure and fields
unchanged aside from the prefix.
🧹 Nitpick comments (7)
app/src/lib/i18n/zh-CN.ts (1)

3673-3674: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistent term for "agent" within the new template strings.

These new template descriptions use 代理 for "agent" ('代理编写简短摘要并发布到频道。', '传入的 Webhook 由代理分流,然后通知你。', '询问代理' / '一个简单的手动触发器,将任务交给代理。'), while the rest of this same PR's flows.* namespace consistently uses 智能体 for "agent" (e.g. flows.nodeKind.agent: '智能体', and the newly added flows.nodeConfig.agent.promptLabel: '给智能体的指令…'). Recommend aligning these three template strings to 智能体 for consistency.

🌐 Proposed fix
-  'flows.templates.daily-digest.description': '按计划,代理编写简短摘要并发布到频道。',
+  'flows.templates.daily-digest.description': '按计划,智能体编写简短摘要并发布到频道。',
   'flows.templates.scheduled-scrape.name': '定时抓取到记忆',
   'flows.templates.scheduled-scrape.description': '按计划获取来源,重塑结果,并存储到记忆中。',
-  'flows.templates.webhook-triage.name': 'Webhook 分流与通知',
-  'flows.templates.webhook-triage.description': '传入的 Webhook 由代理分流,然后通知你。',
+  'flows.templates.webhook-triage.description': '传入的 Webhook 由智能体分流,然后通知你。',
   ...
-  'flows.templates.ask-agent.name': '询问代理',
-  'flows.templates.ask-agent.description': '一个简单的手动触发器,将任务交给代理。',
+  'flows.templates.ask-agent.name': '询问智能体',
+  'flows.templates.ask-agent.description': '一个简单的手动触发器,将任务交给智能体。',

Also applies to: 3677-3678, 3683-3684

🤖 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/lib/i18n/zh-CN.ts` around lines 3673 - 3674, The new flows.template
strings use 代理 for “agent”, but the rest of the flows namespace in this PR uses
智能体, so align these template descriptions for consistency. Update the affected
translation entries in zh-CN.ts for the daily-digest, webhook relay, and
ask-agent templates to use 智能体 wording, following the existing terminology used
by flows.nodeKind.agent and flows.nodeConfig.agent.promptLabel.
src/openhuman/skills/schemas/wire_types.rs (1)

251-253: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Struct name still carries old Workflows prefix.

Field type was renamed to skills::run_log::ScannedRun, but the struct itself is still WorkflowsRecentRunsResult even though the corresponding schema is now skills_recent_runs. Consider renaming for consistency with the rest of the renamed surface (e.g. SkillsRecentRunsResult), unless this is intentionally kept for wire/serialization compatibility.

🤖 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/skills/schemas/wire_types.rs` around lines 251 - 253, The
result struct still uses the old Workflows prefix even though the schema and
field type were renamed, so update the name in the wire_types module for
consistency. Rename WorkflowsRecentRunsResult to match the new
skills_recent_runs surface (for example, SkillsRecentRunsResult) and check any
references to this struct so the renamed symbol is used consistently unless the
old name is required for serialization compatibility.
app/src/components/flows/RunItemDataBrowser.tsx (1)

263-265: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

scope="row" is invalid on <td>.

The scope attribute only applies to <th> elements; on a <td> it's silently ignored, so no row-header semantics are actually conveyed to assistive tech.

♿ Proposed fix
-        <td className="px-1.5 py-1 font-mono text-content-faint" scope="row">
+        <th className="px-1.5 py-1 font-mono text-content-faint font-normal text-left" scope="row">
           {index + 1}
-        </td>
+        </th>
🤖 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/flows/RunItemDataBrowser.tsx` around lines 263 - 265, The
row index cell in RunItemDataBrowser is using scope on a td, which is invalid
and does not provide row-header semantics. Update the table markup in the cell
that renders index + 1 so that scope is removed from the td, and if row-header
behavior is intended, move the semantics to a th element in that same row
rendering instead.
app/src/components/flows/NewWorkflowModal.tsx (1)

71-92: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider logging view-transition actions too.

startFromScratch/startFromTemplate log via debug, but openGallery/backToChooser (and the onDescribe hand-off) don't. As per coding guidelines, "On new or changed flows, add verbose debug logging with stable prefixes, correlation fields... incomplete changes are those lacking logging." Minor gap for tracing the chooser flow end-to-end.

🤖 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/flows/NewWorkflowModal.tsx` around lines 71 - 92, Add
missing verbose debug logging for the chooser flow transitions in
NewWorkflowModal so the flow can be traced end-to-end. Mirror the existing
logging style used by startFromScratch and startFromTemplate by adding
stable-prefix debug entries in openGallery, backToChooser, and the onDescribe
hand-off path. Include correlation-friendly fields such as the selected template
id or target view where applicable, and keep the messages consistent with the
existing log() usage in NewWorkflowModal.

Source: Path instructions

app/src/components/flows/WorkflowCopilotPanel.tsx (1)

70-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate submit/keyboard-handling logic vs WorkflowPromptBar.tsx.

The submit/onKeyDown (Enter-to-submit, Shift+Enter-for-newline, disabled-while-sending) logic here is nearly identical to WorkflowPromptBar.tsx (lines 32-48). Consider extracting a small shared hook (e.g. usePromptSubmit(onSubmit, disabled)) to avoid drift between the two authoring surfaces.

🤖 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/flows/WorkflowCopilotPanel.tsx` around lines 70 - 96, The
submit and Enter-key handling in WorkflowCopilotPanel duplicates the same logic
used in WorkflowPromptBar, so refactor this shared behavior into a reusable hook
or helper such as usePromptSubmit. Move the trimmed submit, sending guard, and
Enter/Shift+Enter keyboard handling into the shared abstraction, then have
WorkflowCopilotPanel consume it instead of keeping its own copy, so both
authoring surfaces stay aligned.
app/src/components/flows/canvas/EditableFlowCanvas.tsx (1)

159-225: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Full graph re-serialization (JSON.stringify) runs on every drag frame.

currentGraph/currentKey (Line 159-163) recompute on every nodes/edges change, including the continuous position updates xyflow emits while a user drags a node. Each recompute re-serializes the entire graph, and drives the onGraphChange effect (Line 168-171) to also fire on every drag tick — which, per the Phase 5c comment, can be forwarded to the copilot host on every pixel of movement. For larger graphs this could produce a noticeable performance hit and needless downstream work during interactive dragging.

Consider gating the expensive recompute/notification on drag-end (e.g., skip while any node has xyflow's dragging: true, or debounce via useDeferredValue) rather than on every intermediate position tick.

♻️ Sketch: skip recompute while a node is actively being dragged
+  const isDragging = nodes.some(n => n.dragging);
   const currentGraph = useMemo(
-    () => xyflowToWorkflowGraph(nodes, edges, meta),
-    [nodes, edges, meta]
+    () => xyflowToWorkflowGraph(nodes, edges, meta),
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    [isDragging ? null : nodes, edges, meta]
   );
🤖 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/flows/canvas/EditableFlowCanvas.tsx` around lines 159 -
225, The graph serialization in EditableFlowCanvas is being recomputed and
broadcast on every drag tick, which causes unnecessary JSON.stringify work and
repeated onGraphChange calls. Update the currentGraph/currentKey flow to skip or
defer recomputation while xyflow nodes are actively dragging (for example by
checking each node’s dragging state or debouncing the update), and keep the live
notification tied to the settled graph state in the existing
useEffect/onGraphChange path.
app/src/components/flows/canvas/FlowCanvas.tsx (1)

104-119: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Hoist the default meta fallback to a stable reference.

meta ?? { schema_version: 1, name: '' } allocates a new object every render when meta is omitted, defeating EditableFlowCanvas's useMemo dependency on meta (Lines 159-175 in EditableFlowCanvas.tsx) and forcing repeat graph serialization on every parent re-render even with no actual edits.

♻️ Proposed fix
+const DEFAULT_META: WorkflowGraphMeta = { schema_version: 1, name: '' };
+
 function FlowCanvas({
   nodes,
   edges,
   editable = false,
   meta,
   ...
 }: FlowCanvasProps) {
   if (editable) {
     return (
       <EditableFlowCanvas
         nodes={nodes}
         edges={edges}
-        meta={meta ?? { schema_version: 1, name: '' }}
+        meta={meta ?? DEFAULT_META}
🤖 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/flows/canvas/FlowCanvas.tsx` around lines 104 - 119, The
`meta ?? { schema_version: 1, name: '' }` fallback in `FlowCanvas` creates a new
object on every render, causing `EditableFlowCanvas` to see a changing `meta`
prop and rerun its `useMemo` work unnecessarily. Hoist that default into a
stable reference in `FlowCanvas` (or a shared constant) and pass the same object
instance whenever `meta` is absent, so `EditableFlowCanvas` can rely on
referential equality and avoid repeated graph serialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/chat/WorkflowProposalCard.test.tsx`:
- Around line 11-22: The Vitest mock factories in WorkflowProposalCard.test.tsx
close over mockUpdateFlow, mockDispatch, and mockNavigate before those handles
are initialized because vi.mock is hoisted. Move all mock handles referenced by
the vi.mock factories into a vi.hoisted(...) block, then keep the existing
createFlow, updateFlow, useAppDispatch, and useNavigate mocks wired to those
hoisted handles.

In `@app/src/components/chat/WorkflowProposalCard.tsx`:
- Line 56: The `openInCanvas` debug log in `WorkflowProposalCard` is leaking
user-provided workflow text via `proposal.name`. Keep the stable prefix and
`threadId`, but replace the name with non-sensitive shape data (for example, a
boolean, length, or count-based summary) in the `log(...)` call. Update the
logging in this flow to follow the “no secrets or full PII” guideline while
preserving correlation and debugging usefulness.

In `@app/src/components/flows/canvas/useFlowValidation.ts`:
- Around line 66-85: The validation flow in useFlowValidation’s run callback can
apply an out-of-order result, so add a monotonic request token/sequence guard
around validateFlow(graphRef.current) and only call setValidation for the latest
in-flight request. Update the run/validateNow path to compare the active token
before logging or setting state, and keep the verbose debug log in place with a
stable prefix plus the new correlation field for each validation attempt.

In `@app/src/hooks/useFlowRunProgress.ts`:
- Around line 102-115: The effect in useFlowRunProgress is resetting statuses
synchronously with setStatuses({}) at the top, which violates the repo’s
react-hooks/set-state-in-effect pattern. Move the “clear previous run state when
runId changes” logic out of the useEffect in useFlowRunProgress and into
render-phase reset handling, using a ref to detect when runId changes and
trigger the reset during render instead. Keep the subscription/unsubscription
logic in the effect tied to runId and handleProgress, but remove the
unconditional state update from the effect body.

In `@app/src/hooks/useWorkflowBuilderChat.ts`:
- Around line 105-143: The send failure cleanup in useWorkflowBuilderChat leaves
a newly created thread active because the catch block only clears state when the
original threadId exists. Update the send flow so the actual targetThreadId
created in the success path is available to the catch block, and use it to clear
runtime/inference state after failures from createNewThread, addMessageLocal, or
chatSend. Make the cleanup in send consistent with the existing
beginInferenceTurn, markThreadInferenceActive, clearRuntimeForThread, and
clearThreadInferenceActive calls so a freshly created builder thread is always
reset on error.

In `@src/openhuman/flows/mod.rs`:
- Line 10: The flow workflow-builder tools should not be exposed as a separate
domain module; move the `builder_tools` implementation under the flow domain’s
`tools.rs` (or a submodule it re-exports) and remove the standalone `pub mod
builder_tools` from `mod.rs`. Update the flow tool wiring so any builder-related
types/functions remain accessible through the existing `tools` module and are
re-exported via `src/openhuman/tools/mod.rs` in line with the domain-owned tool
pattern.

In `@src/openhuman/flows/n8n_import.rs`:
- Around line 403-414: The synthetic trigger node in the n8n import flow uses a
fixed id of "trigger", which can collide with an existing imported node id and
break validation. Update the trigger insertion logic in the n8n_import flow to
generate a unique, collision-free id before calling nodes.insert, reusing the
existing import/reconciliation context in this area so the synthetic Trigger
node remains distinct from all imported nodes.
- Around line 337-375: The json_path_to_jq helper is dropping quotes when
converting bracketed JSON keys, which breaks jq for keys with spaces or
punctuation. Update json_path_to_jq to detect non-bare identifiers and emit
quoted field access instead of plain dot notation, while keeping bare
identifiers unchanged; adjust the bracket-key handling in json_path_to_jq so
cases like ["first name"] stay valid for downstream jq parsing.

---

Outside diff comments:
In `@src/openhuman/skills/schemas/handlers.rs`:
- Around line 32-66: The tracing labels in the renamed skills RPC handlers still
use the old “[workflows][rpc]” prefix, making logs inconsistent with the rest of
the file. Update the debug calls in handle_skills_list, handle_skills_cancel,
and handle_skills_update to use the “[skills][rpc]” prefix everywhere those
messages are emitted, matching the other handlers in this module. Keep the
existing log structure and fields unchanged aside from the prefix.

---

Nitpick comments:
In `@app/src/components/flows/canvas/EditableFlowCanvas.tsx`:
- Around line 159-225: The graph serialization in EditableFlowCanvas is being
recomputed and broadcast on every drag tick, which causes unnecessary
JSON.stringify work and repeated onGraphChange calls. Update the
currentGraph/currentKey flow to skip or defer recomputation while xyflow nodes
are actively dragging (for example by checking each node’s dragging state or
debouncing the update), and keep the live notification tied to the settled graph
state in the existing useEffect/onGraphChange path.

In `@app/src/components/flows/canvas/FlowCanvas.tsx`:
- Around line 104-119: The `meta ?? { schema_version: 1, name: '' }` fallback in
`FlowCanvas` creates a new object on every render, causing `EditableFlowCanvas`
to see a changing `meta` prop and rerun its `useMemo` work unnecessarily. Hoist
that default into a stable reference in `FlowCanvas` (or a shared constant) and
pass the same object instance whenever `meta` is absent, so `EditableFlowCanvas`
can rely on referential equality and avoid repeated graph serialization.

In `@app/src/components/flows/NewWorkflowModal.tsx`:
- Around line 71-92: Add missing verbose debug logging for the chooser flow
transitions in NewWorkflowModal so the flow can be traced end-to-end. Mirror the
existing logging style used by startFromScratch and startFromTemplate by adding
stable-prefix debug entries in openGallery, backToChooser, and the onDescribe
hand-off path. Include correlation-friendly fields such as the selected template
id or target view where applicable, and keep the messages consistent with the
existing log() usage in NewWorkflowModal.

In `@app/src/components/flows/RunItemDataBrowser.tsx`:
- Around line 263-265: The row index cell in RunItemDataBrowser is using scope
on a td, which is invalid and does not provide row-header semantics. Update the
table markup in the cell that renders index + 1 so that scope is removed from
the td, and if row-header behavior is intended, move the semantics to a th
element in that same row rendering instead.

In `@app/src/components/flows/WorkflowCopilotPanel.tsx`:
- Around line 70-96: The submit and Enter-key handling in WorkflowCopilotPanel
duplicates the same logic used in WorkflowPromptBar, so refactor this shared
behavior into a reusable hook or helper such as usePromptSubmit. Move the
trimmed submit, sending guard, and Enter/Shift+Enter keyboard handling into the
shared abstraction, then have WorkflowCopilotPanel consume it instead of keeping
its own copy, so both authoring surfaces stay aligned.

In `@app/src/lib/i18n/zh-CN.ts`:
- Around line 3673-3674: The new flows.template strings use 代理 for “agent”, but
the rest of the flows namespace in this PR uses 智能体, so align these template
descriptions for consistency. Update the affected translation entries in
zh-CN.ts for the daily-digest, webhook relay, and ask-agent templates to use 智能体
wording, following the existing terminology used by flows.nodeKind.agent and
flows.nodeConfig.agent.promptLabel.

In `@src/openhuman/skills/schemas/wire_types.rs`:
- Around line 251-253: The result struct still uses the old Workflows prefix
even though the schema and field type were renamed, so update the name in the
wire_types module for consistency. Rename WorkflowsRecentRunsResult to match the
new skills_recent_runs surface (for example, SkillsRecentRunsResult) and check
any references to this struct so the renamed symbol is used consistently unless
the old name is required for serialization compatibility.
🪄 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: 9179b5d7-c05c-4b0f-848d-e19a9c28bb2c

📥 Commits

Reviewing files that changed from the base of the PR and between e3906df and 627f33e.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • app/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (191)
  • Cargo.toml
  • app/src/AppRoutes.auth.test.tsx
  • app/src/AppRoutes.tsx
  • app/src/components/chat/WorkflowProposalCard.test.tsx
  • app/src/components/chat/WorkflowProposalCard.tsx
  • app/src/components/flows/FlowListRow.test.tsx
  • app/src/components/flows/FlowListRow.tsx
  • app/src/components/flows/FlowRunInspectorDrawer.tsx
  • app/src/components/flows/FlowRunsDrawer.tsx
  • app/src/components/flows/FlowTemplateGallery.tsx
  • app/src/components/flows/NewWorkflowModal.test.tsx
  • app/src/components/flows/NewWorkflowModal.tsx
  • app/src/components/flows/RunItemDataBrowser.tsx
  • app/src/components/flows/WorkflowCopilotPanel.test.tsx
  • app/src/components/flows/WorkflowCopilotPanel.tsx
  • app/src/components/flows/WorkflowPromptBar.test.tsx
  • app/src/components/flows/WorkflowPromptBar.tsx
  • app/src/components/flows/__tests__/FlowRunInspectorDrawer.test.tsx
  • app/src/components/flows/__tests__/RunItemDataBrowser.test.tsx
  • app/src/components/flows/canvas/EditableFlowCanvas.tsx
  • app/src/components/flows/canvas/FlowCanvas.tsx
  • app/src/components/flows/canvas/FlowNodeComponent.tsx
  • app/src/components/flows/canvas/FlowValidationBanner.tsx
  • app/src/components/flows/canvas/NodePalette.tsx
  • app/src/components/flows/canvas/__tests__/EditableFlowCanvas.runOverlay.test.tsx
  • app/src/components/flows/canvas/__tests__/EditableFlowCanvas.test.tsx
  • app/src/components/flows/canvas/__tests__/EditableFlowCanvas.validation.test.tsx
  • app/src/components/flows/canvas/flowCanvasStyles.css
  • app/src/components/flows/canvas/nodeConfig/NodeConfigDrawer.tsx
  • app/src/components/flows/canvas/nodeConfig/__tests__/NodeConfigDrawer.test.tsx
  • app/src/components/flows/canvas/nodeConfig/__tests__/nodeConfigForms.test.tsx
  • app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx
  • app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx
  • app/src/components/flows/canvas/useFlowValidation.ts
  • app/src/components/flows/useCreateFlow.ts
  • 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/InstallSkillDialog.tsx
  • app/src/components/skills/SkillsExplorerTab.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__/CreateWorkflowForm.test.tsx
  • app/src/components/skills/__tests__/InstallSkillDialog.test.tsx
  • app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx
  • app/src/components/skills/__tests__/UninstallSkillConfirmDialog.test.tsx
  • app/src/components/skills/__tests__/WorkflowRunnerBody.test.tsx
  • app/src/components/skills/preflightGate.ts
  • app/src/hooks/useFlowRunProgress.test.ts
  • app/src/hooks/useFlowRunProgress.ts
  • app/src/hooks/useWorkflowBuilderChat.test.ts
  • app/src/hooks/useWorkflowBuilderChat.ts
  • app/src/lib/flows/canvasDraft.ts
  • app/src/lib/flows/exportFlow.test.ts
  • app/src/lib/flows/exportFlow.ts
  • app/src/lib/flows/flowValidation.test.ts
  • app/src/lib/flows/flowValidation.ts
  • app/src/lib/flows/graphAdapter.test.ts
  • app/src/lib/flows/graphAdapter.ts
  • app/src/lib/flows/graphDiff.test.ts
  • app/src/lib/flows/graphDiff.ts
  • app/src/lib/flows/newFlow.test.ts
  • app/src/lib/flows/newFlow.ts
  • app/src/lib/flows/nodeKindMeta.ts
  • app/src/lib/flows/runItems.test.ts
  • app/src/lib/flows/runItems.ts
  • app/src/lib/flows/templates/app-event-route.json
  • app/src/lib/flows/templates/ask-agent.json
  • app/src/lib/flows/templates/daily-digest.json
  • app/src/lib/flows/templates/http-fetch-parse.json
  • app/src/lib/flows/templates/index.ts
  • app/src/lib/flows/templates/scheduled-scrape.json
  • app/src/lib/flows/templates/templates.test.ts
  • app/src/lib/flows/templates/webhook-triage.json
  • app/src/lib/flows/workflowBuilderPrompt.test.ts
  • app/src/lib/flows/workflowBuilderPrompt.ts
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/pages/FlowCanvasPage.tsx
  • app/src/pages/FlowsPage.test.tsx
  • app/src/pages/FlowsPage.tsx
  • app/src/pages/WorkflowNew.test.tsx
  • app/src/pages/WorkflowNew.tsx
  • app/src/pages/__tests__/FlowCanvasPage.test.tsx
  • app/src/pages/__tests__/Skills.channels-grid.test.tsx
  • app/src/pages/__tests__/Skills.mcp-coming-soon.test.tsx
  • app/src/pages/__tests__/Skills.meetings-tab.test.tsx
  • app/src/providers/ChatRuntimeProvider.tsx
  • app/src/services/api/__tests__/flowsApi.test.ts
  • app/src/services/api/__tests__/skillsApi.test.ts
  • app/src/services/api/flowsApi.ts
  • app/src/services/api/skillsApi.test.ts
  • app/src/services/api/skillsApi.ts
  • app/src/test/test-utils.tsx
  • app/test/e2e/specs/flows.spec.ts
  • src/core/all.rs
  • src/core/jsonrpc.rs
  • src/openhuman/agent/harness/builtin_definitions.rs
  • src/openhuman/agent/harness/fork_context.rs
  • src/openhuman/agent/harness/session/builder/factory.rs
  • src/openhuman/agent/harness/session/builder/setters.rs
  • src/openhuman/agent/harness/session/runtime.rs
  • src/openhuman/agent/harness/session/runtime_tests.rs
  • src/openhuman/agent/harness/session/tests.rs
  • src/openhuman/agent/harness/session/turn/tools.rs
  • src/openhuman/agent/harness/session/turn_tests.rs
  • src/openhuman/agent/harness/session/types.rs
  • src/openhuman/agent/prompts/render_helpers.rs
  • src/openhuman/agent/prompts/types.rs
  • src/openhuman/agent/task_dispatcher/executor.rs
  • src/openhuman/agent/tools/run_workflow.rs
  • src/openhuman/agent_registry/agents/loader.rs
  • src/openhuman/agent_registry/agents/mod.rs
  • src/openhuman/agent_registry/agents/orchestrator/agent.toml
  • src/openhuman/agent_registry/agents/orchestrator/prompt.rs
  • src/openhuman/agent_registry/agents/workflow_builder/agent.toml
  • src/openhuman/agent_registry/agents/workflow_builder/mod.rs
  • src/openhuman/agent_registry/agents/workflow_builder/prompt.md
  • src/openhuman/agent_registry/agents/workflow_builder/prompt.rs
  • src/openhuman/channels/runtime/startup.rs
  • src/openhuman/channels/tests/prompt.rs
  • src/openhuman/context/channels_prompt.rs
  • src/openhuman/flows/builder_tools.rs
  • src/openhuman/flows/builder_tools_tests.rs
  • src/openhuman/flows/mod.rs
  • src/openhuman/flows/n8n_import.rs
  • src/openhuman/flows/ops.rs
  • src/openhuman/flows/ops_tests.rs
  • src/openhuman/flows/schemas.rs
  • src/openhuman/flows/store.rs
  • src/openhuman/flows/store_tests.rs
  • src/openhuman/flows/tools.rs
  • src/openhuman/flows/types.rs
  • src/openhuman/mcp_client/client.rs
  • src/openhuman/mod.rs
  • src/openhuman/runtime_node/ops.rs
  • src/openhuman/runtime_node/types.rs
  • src/openhuman/skill_registry/ops.rs
  • src/openhuman/skill_registry/schemas/handlers.rs
  • src/openhuman/skill_registry/schemas/wire_types.rs
  • src/openhuman/skill_registry/tools.rs
  • src/openhuman/skill_runtime/run_machinery.rs
  • src/openhuman/skill_runtime/schemas.rs
  • src/openhuman/skills/README.md
  • src/openhuman/skills/bus.rs
  • src/openhuman/skills/e2e_plumbing_tests.rs
  • src/openhuman/skills/e2e_run_tests.rs
  • src/openhuman/skills/mod.rs
  • src/openhuman/skills/ops.rs
  • src/openhuman/skills/ops_create.rs
  • src/openhuman/skills/ops_discover.rs
  • src/openhuman/skills/ops_install.rs
  • src/openhuman/skills/ops_parse.rs
  • src/openhuman/skills/ops_tests.rs
  • src/openhuman/skills/ops_types.rs
  • src/openhuman/skills/preflight.rs
  • src/openhuman/skills/registry.rs
  • src/openhuman/skills/run_log.rs
  • src/openhuman/skills/schemas/controller_schemas.rs
  • src/openhuman/skills/schemas/handlers.rs
  • src/openhuman/skills/schemas/helpers.rs
  • src/openhuman/skills/schemas/mod.rs
  • src/openhuman/skills/schemas/wire_types.rs
  • src/openhuman/skills/schemas_tests.rs
  • src/openhuman/skills/tools.rs
  • src/openhuman/skills/types.rs
  • src/openhuman/tinyflows/caps.rs
  • src/openhuman/tools/impl/presentation/tests.rs
  • src/openhuman/tools/mod.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs
  • src/openhuman/tools/traits.rs
  • src/openhuman/workspace/ops.rs
  • tests/agent_round26_raw_coverage_e2e.rs
  • tests/json_rpc_e2e.rs
  • tests/x402_twit_sh_live.rs
  • vendor/tinyflows
💤 Files with no reviewable changes (3)
  • app/src/pages/WorkflowNew.tsx
  • app/src/pages/WorkflowNew.test.tsx
  • app/src/AppRoutes.auth.test.tsx

Comment thread app/src/hooks/useFlowRunProgress.ts Resolved
Comment thread app/src/hooks/useWorkflowBuilderChat.ts Resolved
//! [`crate::openhuman::tinyflows::caps::FlowStateStore`]); the RPC/CLI
//! controller surface in `schemas` (private, re-exported below).

pub mod builder_tools;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Keep workflow-builder tools under the domain tool module.

Adding a separate builder_tools module creates a second domain-owned tool implementation surface for flows. Move these tools behind src/openhuman/flows/tools.rs or an allowed submodule re-exported from there. As per coding guidelines, "Domain-owned agent tools must be implemented in that domain's tools.rs and re-exported via src/openhuman/tools/mod.rs; only cross-cutting families belong in tools/impl/."

🤖 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/flows/mod.rs` at line 10, The flow workflow-builder tools
should not be exposed as a separate domain module; move the `builder_tools`
implementation under the flow domain’s `tools.rs` (or a submodule it re-exports)
and remove the standalone `pub mod builder_tools` from `mod.rs`. Update the flow
tool wiring so any builder-related types/functions remain accessible through the
existing `tools` module and are re-exported via `src/openhuman/tools/mod.rs` in
line with the domain-owned tool pattern.

Source: Coding guidelines

Comment thread src/openhuman/flows/n8n_import.rs Resolved
Comment thread src/openhuman/flows/n8n_import.rs Resolved

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review


P2 Badge Advertise the actual skills list payload name

For schema consumers of /schema, this says openhuman.skills_list returns a skills field, but handle_skills_list serializes WorkflowsListResult { workflows: ... } and the frontend/tests read result.workflows. Generated clients following the new schema will see undefined; the schema should match the wire payload, or the handler and frontend need to be renamed together.

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread app/src/hooks/useWorkflowBuilderChat.ts Outdated
Comment on lines +140 to +142
if (threadId) {
dispatch(clearRuntimeForThread({ threadId }));
dispatch(clearThreadInferenceActive(threadId));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear the newly created builder thread on send failure

When the first prompt/canvas copilot send creates a new thread and chatSend rejects (for example, the socket drops after the initial connected check or the RPC fails), threadId in this closure is still the previous state (null), so this branch skips clearing the active runtime markers that were set for the local targetThreadId. The hook then stores the new thread id and runtimeActive keeps sending true, leaving the prompt bar/copilot disabled until it is remounted; clear using the local thread id that was actually marked active.

Useful? React with 👍 / 👎.

type_version: 1,
name: n8n_name,
config,
ports: Vec::new(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Seed imported condition nodes with branch ports

The fresh evidence is this importer still writes ports: Vec::new() for imported IF nodes: when an n8n IF/condition currently has only one branch wired, the canvas derives output handles from declared ports plus existing edges, so it exposes only the already-wired true or false handle and the user cannot add the missing branch from the UI. Condition nodes imported here should declare both true and false ports just like palette-created conditions.

Useful? React with 👍 / 👎.

senamakel added 7 commits July 5, 2026 11:15
…mocks

WorkflowProposalCard's openInCanvas log leaked proposal.name (user-provided
text) — replace with node/edge counts, which is enough to correlate without
carrying PII. Also hoist WorkflowProposalCard.test.tsx's mock handles into
vi.hoisted(): they were referenced by vi.mock factories (which Vitest hoists
above the module body) from plain top-level consts, which only worked because
Vitest's compiler special-cases `mock`-prefixed identifiers — not a guarantee
(addresses @coderabbitai on WorkflowProposalCard.tsx:56 and .test.tsx:22).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
run() had no protection against a stale validation response overwriting a
fresher one — a debounced auto-validate racing a manual validateNow() (or two
rapid validateNow() calls) let the last response to RESOLVE win regardless of
issue order, which could wrongly unblock or re-block Save after further edits.
Adds a monotonic request-id ref: a response only commits (and clears
`validating`) if it's still the latest in-flight request; the id also
becomes the log's correlation field
(addresses @coderabbitai on useFlowValidation.ts:85).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
…fect

setStatuses({}) ran unconditionally at the top of the subscribe effect body
(before the `if (!runId) return`), which react-hooks/set-state-in-effect
(enforced in this codebase) disallows — synchronous setState inside an effect
causes a cascading extra render. Moved the "new runId → drop prior run's node
states" reset to render phase via the standard ref-comparison pattern
(addresses @coderabbitai on useFlowRunProgress.ts:115).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
The catch block guarded cleanup on the outer `threadId` state, but for a
send() call that itself creates a new thread, `threadId` state doesn't update
synchronously within that same closure invocation — so a failure after
creation (e.g. addMessageLocal/chatSend rejecting) saw the stale `null`,
skipped clearRuntimeForThread/clearThreadInferenceActive, and left the new
thread's active markers dangling. Hoisted `targetThreadId` above the try so
the catch block sees the actual thread this call operated on
(addresses @coderabbitai on useWorkflowBuilderChat.ts:143).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
… on import

Two n8n-import bugs: (1) json_path_to_jq emitted unquoted dot access for
bracket keys with spaces/punctuation (e.g. `["first name"]` -> `.first name`),
which is invalid jq — now emits jq's dot-plus-quoted-string form for any key
that isn't a bare identifier. (2) the synthesized manual trigger always used
the literal id "trigger", which collides and fails validation if the source
n8n graph already has a differently-kinded node with that id — now probes for
a free `trigger`/`trigger_2`/... id
(addresses @coderabbitai on n8n_import.rs:375 and :414).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
handle_skills_list/cancel/update still emitted "[workflows][rpc] ..." tracing
labels while every other renamed handler in this file uses "[skills][rpc] ...",
making log-based debugging inconsistent for the renamed RPC surface
(addresses @coderabbitai outside-diff comment on handlers.rs:32-66).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
… consistency

- RunItemDataBrowser: scope="row" is invalid on <td> (silently ignored by
  assistive tech) — the index cell is now a <th scope="row"> so it actually
  conveys row-header semantics.
- FlowCanvas: hoist the default `meta` fallback to a module-level constant so
  omitting `meta` doesn't allocate a new object every render, which was
  defeating EditableFlowCanvas's `useMemo(..., [meta])` and forcing a full
  graph re-serialization on every parent re-render even with no real edit.
- zh-CN: align three new flows.templates.* strings on 智能体 for "agent",
  matching flows.nodeKind.agent / flows.nodeConfig.agent.* elsewhere in this
  same PR's namespace instead of the inconsistent 代理
(addresses @coderabbitai on RunItemDataBrowser.tsx:263, FlowCanvas.tsx:104,
and zh-CN.ts:3673-3684).

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
@senamakel

Copy link
Copy Markdown
Member Author

Responding to the remaining CodeRabbit findings on this review round:

Fixed (pushed at 5546f1a5c094ad362a3597a7e7b34e29d146d036):

  • src/openhuman/skills/schemas/handlers.rs — stale [workflows][rpc] log prefixes on handle_skills_list/cancel/update now read [skills][rpc], consistent with every other renamed handler in the file.
  • app/src/components/flows/RunItemDataBrowser.tsx — the row-index cell is now a <th scope="row"> (was an invalid <td scope="row">).
  • app/src/components/flows/canvas/FlowCanvas.tsx — hoisted the default meta fallback to a module constant so an omitted meta doesn't force a re-serialize on every parent render.
  • app/src/lib/i18n/zh-CN.ts — aligned the new flows.templates.* strings on 智能体 for "agent", matching the rest of this PR's flows.* namespace.

Deferring (flagging rather than silently dropping):

  • src/openhuman/flows/mod.rs — builder_tools module placement ("Heavy lift"): agreed this is a legitimate organizational question — the domain already has both tools.rs (341 lines) and builder_tools.rs (766 lines) as siblings. I'm not doing a blind mechanical merge of two large, independently-tested tool-implementation files this late in an already-large PR; both stay within the flows:: domain (no cross-domain leak), so this doesn't violate the "domain-owned, not cross-cutting" part of the rule, just the "single tools.rs" expectation. Proposing as a follow-up: either fold builder_tools.rs into a tools/ submodule re-exported from tools.rs, or merge outright if the combined file still comfortably clears the ~500-line guidance (it won't — 341+766 — so submodule is the likely answer). Happy to do this as an immediate fast-follow PR if a maintainer confirms which shape they want.
  • src/openhuman/skills/schemas/wire_types.rs — WorkflowsRecentRunsResult naming: every other param/result struct in this file (WorkflowsListParams, WorkflowsCreateResult, etc. — 16 total) still carries the Workflows prefix; renaming only this one struct would make it the odd one out, not fix an inconsistency. A full struct-name rename across the file is a separate, broader decision than this PR's stated scope (RPC method strings + frontend workflowsApi→skillsApi; internal Rust type names were explicitly left alone) — deferring to a maintainer call.
  • app/src/components/flows/WorkflowCopilotPanel.tsx vs WorkflowPromptBar.tsx — duplicated submit/keyboard handling: real duplication, agreed a shared usePromptSubmit hook is the right cleanup — deferring as a non-blocking refactor-only follow-up (no behavior change, lower risk to do carefully in its own PR).
  • app/src/components/flows/NewWorkflowModal.tsx — missing debug logs on openGallery/backToChooser: low-value per CodeRabbit's own tag; deferring, will fold into the copilot-hook follow-up above if it touches this file anyway.
  • app/src/components/flows/canvas/EditableFlowCanvas.tsx — skip graph re-serialization while dragging: legitimate perf idea, but gating on xyflow's per-node dragging flag interacts with the dirty-baseline/copilot-diff logic in this same file (and the initialDirty fix just pushed) closely enough that I'd rather profile it and land it deliberately in its own PR than risk a regression bundled into this already-large one.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

outputs: vec![FieldSchema {
name: "skills",
ty: TypeSchema::Array(Box::new(TypeSchema::Ref("WorkflowSummary"))),

P2 Badge Make skills_list schema match the wire field

The handler still serializes WorkflowsListResult { workflows: ... } and the frontend intentionally reads result.workflows, but the new controller schema advertises a skills output field. Schema-driven clients and /schema consumers calling openhuman.skills_list will look for the wrong field; keep the schema as workflows or change the wire response and clients together.

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/core/all.rs
controllers.extend(crate::openhuman::javascript::all_javascript_registered_controllers());
// Discovered SKILL.md skills and their bundled resources
controllers.extend(crate::openhuman::workflows::all_workflows_registered_controllers());
controllers.extend(crate::openhuman::skills::all_skills_registered_controllers());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Update stale workflows_list probes after rename

With this registry swap, openhuman.workflows_list is no longer registered; I checked the CI Full WDIO skills shard in app/scripts/e2e-run-all-flows.sh, and it still runs app/test/e2e/specs/skill-lifecycle.spec.ts, which calls callOpenhumanRpc('openhuman.workflows_list', {}). That shard will now fail with method-not-found unless the spec is updated to openhuman.skills_list or a compatibility alias is kept for the old method.

Useful? React with 👍 / 👎.

Comment on lines +215 to +217
const initialDirty = useMemo(
() => JSON.stringify(editorGraph) !== JSON.stringify(persistedGraphRef.current),
[editorGraph]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mark draft canvases dirty on first render

When the canvas is opened through /flows/draft from an imported file or chat proposal, there is no persisted graph yet, but persistedGraphRef is initialized to that draft graph and this comparison returns false. EditableFlowCanvas disables Save while dirty is false, so users cannot persist an imported/proposed workflow as-is and must make a meaningless edit first; treat flowId === null as initially dirty.

Useful? React with 👍 / 👎.

Comment on lines +286 to +288
const result = await runFlow(flowId);
log('run: started flow id=%s thread_id=%s', flowId, result.thread_id);
setActiveRunId(result.thread_id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Subscribe to run progress before awaiting completion

For canvas runs, runFlow calls openhuman.flows_run, and the backend only returns the thread_id after the engine has settled; because activeRunId is set after this await, the useFlowRunProgress subscription mounts after the observer has already emitted the run's progress events. In any nontrivial run, the new live overlay will miss the events it was added to display; the UI needs a run id before awaiting completion, or the run RPC needs to start asynchronously.

Useful? React with 👍 / 👎.

…ls_*

The "catalog canonical methods exist in core schema registry" test read
src/openhuman/channels/controllers/schemas.rs for the channels_* namespace/
function literals, but tinyhumansai#4557 ("Use tinychannels provider implementations")
moved the actual ChannelControllerSchema definitions (namespace: "channels",
...) into the vendored vendor/tinychannels/src/controllers/schemas.rs crate —
the in-tree file now only adapts from it. This left the drift guard failing
on main independent of this PR (verified: same failure reproduces against
main's own copy of both files); fixed by also reading the vendored schema
source.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU
@senamakel

Copy link
Copy Markdown
Member Author

FYI: the "Frontend Checks" failure on the previous run (5546f1a5c) was NOT from this PR's flows/skills changes — it's app/src/services/__tests__/rpcMethods.test.ts's "catalog canonical methods exist in core schema registry (drift guard)" test, which reads src/openhuman/channels/controllers/schemas.rs for namespace: "channels" literals. #4557 ("Use tinychannels provider implementations", already on main) moved those literals into the vendored vendor/tinychannels/src/controllers/schemas.rs crate, leaving the in-tree adapter file as a thin wrapper with no namespace: "channels" string of its own — so the test fails on main itself, independent of this PR (verified by diffing main's copy of both files against this branch's).

Fixed at 9b623ae48 by also reading the vendored schema source in the test's file list. Flagging per the pattern this task follows for unrelated-but-blocking breakage (like the documented pre-existing i18n debt) — call it out rather than silently absorb it into an unrelated PR's diff.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

description: "Cancel a flow run: settle it to a terminal `cancelled` status, abort \

P2 Badge Handle cancelled flow runs in the frontend

This new run lifecycle introduces the terminal cancelled status, but the frontend wire type/status maps and useFlowRunPoller still only know running, completed, pending_approval, and failed. When a run is cancelled or expires via the parked-run TTL, the run drawers index their status label/class maps with an unknown value and the poller never treats it as terminal, so the inspector can keep polling and render undefined status UI; add cancelled to the frontend status union, mappings, and terminal set.

ℹ️ About Codex in GitHub

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

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

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

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

setBusyKey(key);
setError(null);
try {
const flow = await createFlow(name, graph);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Create templates as drafts or disabled flows

When the user picks a bundled template, this path immediately calls flows_create, whose backend default is enabled = true and which binds automatic triggers right away. For the scheduled templates (daily-digest, scheduled-scrape), that means an incomplete starter graph with placeholder tool slugs/URLs can begin firing before the user has reviewed or configured it; create these as drafts/disabled copies, or require an explicit enable after editing.

Useful? React with 👍 / 👎.

@senamakel
senamakel merged commit 44614a7 into tinyhumansai:main Jul 5, 2026
15 of 16 checks passed
@senamakel

senamakel commented Jul 5, 2026 •

Copy link
Copy Markdown
Member Author

Re Codex's finding on the skills_list schema/wire mismatch: confirmed real bug, fixed at ba4505aba — the controller schema advertised the output field as skills, but the handler still serializes WorkflowsListResult { workflows: ... } (the wire field is deliberately unrenamed, per this PR's own description — only the RPC method name and Rust module path became skills_*). Schema now correctly advertises workflows.

Claude-Session: https://claude.ai/code/session_01QpsTTxxjFgDhzuozS28tyU

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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant