Skip to content

feat(flows): agent-friendliness — editing, drafts, safety rails, wider tools#4876

Merged
senamakel merged 15 commits into
tinyhumansai:mainfrom
senamakel:feat/flows-agent-friendliness
Jul 15, 2026
Merged

feat(flows): agent-friendliness — editing, drafts, safety rails, wider tools#4876
senamakel merged 15 commits into
tinyhumansai:mainfrom
senamakel:feat/flows-agent-friendliness

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

Makes the Flows (Workflows) product agent-friendly, per docs/flows-agent-friendliness-audit.md, in six phased slices (reviewable commit-by-commit):

  • Structured editing & introspection (F1–F3): multi-error validate_all; a queryable node-kind catalog (list_node_kinds / get_node_kind_contract); edit_workflow graph patch-ops (no more whole-graph re-emits); one canonical gate runner + validate_workflow + opt-in strict create/update.
  • Core-managed drafts (F5): durable FlowDraft JSON files shared by the agent tools and canvas (flows_draft_* + promote); edit_workflow/dry_run draft_id mode.
  • Concurrency & observability (F6): optimistic concurrency (expected_version → structured version_conflict); FlowChangedflow:changed socket + useFlowChanged; revision history + flows_rollback + get_flow_history.
  • Wider agent tools behind the new rails (F4/F7): list_flow_runs/resume_flow_run/cancel_flow_run; gated create_workflow (born disabled) + duplicate_flow; dry_run allowed on read-only.
  • Builder UX (connectors): flows_search_tool_catalog/get_tool_contract RPCs, required_connections on the proposal + RPC, list_connectable_toolkits.
  • Naming/prompt hygiene (F8): legacy skills create_workflowcreate_skill (frees the name for Flows; fixes a registry collision); prompt points at the new introspection tools.

Problem

An AI agent creating/editing/testing/debugging a flow hit eight friction points (F1–F8): whole-graph blobs were the only edit unit, the DSL had no queryable schema, validation stopped at the first error, agent and UI saves validated differently, there was no shared durable draft, last-write-wins clobbered concurrent edits with no history, the agent couldn't test or self-debug what it built, and "workflow" meant four different things. See the audit doc for the full findings.

Solution

  • Model-level logic lives in the tinyflows submodule (host-agnostic — validate_all, the node-kind catalog, graph_ops), merged upstream as feat: multi-error validation, node-kind catalog, graph patch-ops tinyflows#11 and pointed at via the submodule bump. The host stays thin: it overlays Composio/oh:/dispatch facts and wires the agent tools + RPCs.
  • Safety-first ordering: the versioning/events/history rails (Phase 3) land before the wider write tools (Phase 4) they make safe. Human-in-the-loop invariants are preserved throughout — agent-created flows are born disabled, the forced require_approval floor stays uncloseable, and propose → user accepts → save remains the default chat flow.
  • One gate definition (ops::run_builder_gates) is shared by propose/revise/edit/save and the strict RPC path, so agent and UI saves can no longer drift.

Scoped follow-ups (called out so they aren't mistaken for gaps): the canvas draft-loading UI (/flows/draft/:draftId), the in-canvas tool browser + Connect-CTA rendering, server-side workflow_copilot thread tagging, run_flow draft_id + a real read-scope test_run mode, and the broader list_workflows/run_workflow*_skill rename (special-cased across the harness, so it warrants its own PR).

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — ~90 new Rust unit/integration tests across the tinyflows crate (validate/catalog/graph_ops) and the host (ops_tests, builder_tools_tests, draft_store, schemas), each covering success + failure/edge paths.
  • Diff coverage ≥ 80% — new Rust ops/tools/schemas paths are unit- and integration-tested; verifying against the CI gate (frontend additions are typed API-client/hook glue).
  • N/A: Coverage matrix — additive agent-tool/RPC surface behind existing user-facing features; the deferred UI (canvas draft-loading, tool browser) is where new matrix rows will land, in the follow-up UI PR.
  • N/A: No matrix feature IDs to list (see above).
  • No new external network dependencies introduced — all new tests use in-memory/temp-dir stores and the existing mock catalog seeding; no real backend/third-party calls.
  • N/A: Manual smoke checklist — no release-cut UI surface changed (the frontend deltas are additive API-client methods + a socket hook + a silent list refetch; the user-facing canvas/tool-browser UI is deferred).
  • N/A: No tracking issue exists for this work; it is driven by docs/flows-agent-friendliness-audit.md (committed on this branch).

Impact

  • Platforms: Rust core (desktop/CLI/headless) — new flows_* RPCs + agent tools; frontend (desktop/web) — additive flowsApi methods, a useFlowChanged socket hook, and a silent FlowsPage refetch. No mobile-specific changes.
  • Persistence/migration: additive flow_revisions SQLite table (created idempotently, no migration of existing rows); drafts are plain JSON files under {workspace}/flows/drafts/ (no schema). Optimistic concurrency reuses updated_at as an etag — no new column.
  • Security: new write tools are permission-gated (create_workflow/duplicate_flow/cancel_flow_run = Write, resume_flow_run = Execute + approval); dry_run downgraded to None because it is mock-only and side-effect-free. flows_search_tool_catalog/get_tool_contract are secret-free.
  • Compatibility: FlowValidation.error_details is additive (serde(default)); the legacy skills create_workflowcreate_skill rename fixes a duplicate-tool-name registry collision.

Related

  • Closes: N/A — no tracking issue; work is specified by docs/flows-agent-friendliness-audit.md on this branch.
  • Depends on: feat: multi-error validation, node-kind catalog, graph patch-ops tinyflows#11 (merged; submodule pointed at f182389).
  • Follow-up PR(s)/TODOs: canvas draft-loading UI; in-canvas tool browser + Connect CTAs; workflow_copilot thread tagging; run_flow draft_id + test_run; the list_workflows/run_workflow*_skill rename.

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

Linear Issue

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

Commit & Branch

  • Branch: feat/flows-agent-friendliness
  • Commit SHA: 3a54ef1

Validation Run

  • pnpm --filter openhuman-app format:check — Prettier auto-fixes applied + committed (pre-push hook)
  • pnpm typecheck — clean
  • Focused tests: cargo test --lib openhuman::flows (327 pass; 1 known pre-existing LIVE_CATALOG_CACHE flake, passes in isolation), tinyflows crate cargo test --features mock (full suite green)
  • Rust fmt/check (if changed): cargo fmt applied; cargo check --bin openhuman-core clean
  • N/A: Tauri fmt/check — app/src-tauri not touched

Validation Blocked

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

Behavior Changes

  • Intended behavior change: agent gains structured editing, durable drafts, a queryable DSL schema, multi-error validation, versioning/rollback, a self-debug loop, and gated create/duplicate — all behind preserved human-in-the-loop gates.
  • User-visible effect: FlowsPage refetches on flow changes (e.g. agent saves) instead of showing stale state; proposals carry required-connection info. Deeper canvas UI is a deferred follow-up.

Parity Contract

  • Legacy behavior preserved: validate() still returns the same first error; propose/revise proposal contract unchanged; flows_create/flows_update last-write-wins by default (strict/expected_version are opt-in); enable/disable stays human-only.
  • Guard/fallback/dispatch parity checks: duplicate-tool-name registry guard green after the create_workflowcreate_skill rename; dry_run still mock-only.

Duplicate / Superseded PR Handling

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

senamakel added 15 commits July 15, 2026 04:24
Surface every structural problem in a candidate graph in one flows_validate
call instead of one round-trip per error — the #1 friction the audit (F3)
calls out for an agent iterating on a flow.

- tinyflows submodule: add validate_all() accumulating all independent
  structural errors; validate() is now its first element (fail-fast contract
  and all existing tests preserved). Add ValidationError::code()/node_id()
  for structured host reporting. (submodule commit 63ad5c4)
- host: split migrate/deserialize (a genuine single failure) from structural
  validation; flows_validate runs validate_all and returns ALL errors.
- add structured FlowValidationError {code, message, node_id, field}; expose
  as additive FlowValidation.error_details (serde default, back-compat — the
  existing errors: Vec<String> now simply carries all messages).

Refs #flows-agent-friendliness F3.
Give agents a machine-readable DSL schema so they need not rely on prompt
prose or memory for node config shapes (audit F2).

- consume tinyflows::catalog (the portable, host-agnostic source of truth)
  and layer a thin host overlay adding only vendor facts: Composio slug
  semantics, oh: native tools, the data-envelope, agent input_context, and
  which trigger kinds actually dispatch here.
- new read-only agent tools list_node_kinds + get_node_kind_contract{kind}
  (the DSL analogue of search_tool_catalog + get_tool_contract), on the
  workflow_builder belt.
- docs-drift guard: a test ties propose_workflow's hand-written description
  to the typed contracts so prose can't silently diverge from code.

Bumps tinyflows submodule to 9ccc141 (catalog module).

Refs #flows-agent-friendliness F2.
Give the agent a cheap, low-regression iteration path (audit F1): change a
saved or draft flow with a small ops[] list instead of re-emitting the whole
graph (token-heavy, the top source of dropped-node/mangled-edge mistakes).

- new edit_workflow agent tool: base = flow_id (saved) or inline graph;
  applies ops via tinyflows::graph_ops::apply_ops; runs validate_all (all
  structural errors at once) + the full hard-gate stack; returns the same
  workflow_proposal payload as revise_workflow. Proposal-only — never
  persists or enables.
- extract ops::build_builder_proposal — the shared gate + summary/warning +
  payload logic — so revise_workflow and edit_workflow can't drift; keeps the
  host tool bodies thin.
- bumps tinyflows submodule to bc68254 (graph_ops module).

Refs #flows-agent-friendliness F1.
Close the gap where agent saves and UI saves validated differently (audit
F3), and give the agent a standalone check.

- ops::run_builder_gates: the single canonical definition of the three
  author hard-gates (binding / tool-contract / required-arg). propose/revise/
  edit (build_builder_proposal) and save_workflow now route through it — no
  more duplicated, drift-prone gate sequences.
- new validate_workflow agent tool: runs the SAME structural + gate stack
  without emitting a proposal, returning { ok, structurally_valid, errors,
  error_details, gate_errors, warnings } so a check is fix-and-retry.
- opt-in strict:bool on flows_create/flows_update RPC (ops::strict_gate):
  runs the agent gates before persisting. Default off keeps the human-canvas
  path permissive; on, the UI plane converges with the agent plane.

Refs #flows-agent-friendliness F3.
A durable, core-managed draft the agent tools and the canvas both read/write
by id across turns and reloads — replacing the client-only, reload-losing
router-state draft.

- FlowDraft stored as plain JSON at {workspace_dir}/flows/drafts/<id>.json
  (no SQLite/migration; atomic temp-write+rename; path-traversal guarded).
- RPC flows_draft_create/get/update/list/delete/promote. promote runs the
  SAME create/update gates (structural validation, forced require_approval
  floor, born-disabled auto-triggers) then removes the draft; never a
  back-door around them.
- edit_workflow gains draft_id mode: base from the draft + the applied edit
  written back to it (the shared working copy). dry_run_workflow gains
  draft_id. flowsApi.ts draft client methods + FlowDraft/error_details types.

Refs #flows-agent-friendliness F5.
…hase 3, F6)

The safety rails that make widening the agent's write surface (Phase 4) safe.

- Optimistic concurrency: update_flow_graph reuses updated_at as an etag;
  flows_update/rollback take expected_version and return a structured
  version_conflict error (carrying the current server flow) instead of
  silently clobbering. flowsApi parseFlowVersionConflict + updateFlow wiring.
- Flow mutation events: DomainEvent::FlowChanged published on
  create/update/delete/enable, bridged to a flow:changed socket event. New
  useFlowChanged hook; FlowsPage refetches on it so an agent save_workflow
  becomes visible instead of silently stale.
- Revision history: flow_revisions table captures the prior graph on every
  update (capped at 20); flows_get_history + flows_rollback RPCs (rollback is
  itself snapshotted, so undoable); get_flow_history agent tool.

Refs #flows-agent-friendliness F6.
… 4, F4/F7)

Now that versioning/events/history make agent writes safe and visible, open
the self-debug loop and gated authoring — all approval/permission gated.

- self-debug loop: list_flow_runs (read), resume_flow_run (Execute +
  approval — advances a real parked run), cancel_flow_run (Write). The agent
  can now find a failing run, diagnose via get_flow_run, patch via
  edit_workflow, and verify — without a run_id handed in externally.
- gated create: create_workflow (Write + approval) persists a NEW flow ALWAYS
  born disabled (enable stays human-only) behind the forced require_approval
  floor and the same author gates as save; duplicate_flow makes a disabled
  copy for clone-then-edit.
- testability: dry_run_workflow is now PermissionLevel::None (mock-only,
  side-effect-free) so a read-only agent can self-verify its own proposal.

Deferred as scoped follow-ups (need new execution plumbing, not tool
wrappers): a real read-scope test_run mode, and run_flow accepting a draft_id.

Refs #flows-agent-friendliness F4 F7.
… UX)

The connector-onboarding and tool-discovery core, reusing the agent-tool
logic so the UI and agent can't drift.

- catalog RPCs (item 16): flows_search_tool_catalog + flows_get_tool_contract
  — thin, secret-free wrappers over the SAME core as the agent's
  search_tool_catalog / get_tool_contract tools. flowsApi searchToolCatalog /
  getToolContract.
- required-connections (item 18): compute_required_connections tags each
  toolkit a graph needs connected|missing (native oh: + http_request skipped);
  surfaced via flows_required_connections RPC AND on the workflow_proposal
  payload, so the proposal card can render Connect CTAs instead of a bare gate
  error. flowsApi requiredConnections.
- agent guidance (item 19): list_connectable_toolkits tool so the agent steers
  toolkit choice toward what's already connected and can enumerate what a flow
  still needs.

Deferred as frontend/cross-domain follow-ups: the in-canvas tool browser UI
(item 17), the Connect-CTA rendering in the proposal/canvas (item 18 UI), and
server-side workflow_copilot thread tagging (item 20).

Refs #flows-agent-friendliness Phase 5.
- Rename the legacy skills tool create_workflow -> create_skill (audit F8):
  'workflow' now unambiguously means a Flows automation. This also fixes the
  hard registry collision Phase 4 introduced when the flows domain claimed the
  create_workflow name — the duplicate-tool-name guard is green again. Updates
  the Skills domain-group list, the user_filter tool families, and the
  domain-membership tests.
- workflow_builder prompt: add a concise 'authoring tools' section pointing at
  the new belt (list_node_kinds/get_node_kind_contract for the DSL schema,
  edit_workflow for cheap iteration, validate_workflow to self-check,
  list_connectable_toolkits, the debug-loop + create/duplicate tools, draft_id
  mode); point the 12-node-kind reference at the introspection tools as the
  source of truth.

Scoped follow-up: the broader list_workflows->list_skills / run_workflow->
run_skill rename (with deprecation aliases) + removing the orchestrator
disambiguation paragraph — run_workflow is special-cased across the harness
turn handler, rhai bridge, and agent tomls, so it warrants its own reviewed
change rather than riding this PR.

Refs #flows-agent-friendliness F8.
The validate_all / catalog / graph_ops changes this branch depends on merged
upstream (tinyhumansai/tinyflows#11, squash-merged as f182389). Point the
submodule at the merged commit so CI can check it out — the earlier per-phase
pointers referenced the pre-merge branch commits, which are not on the
tinyflows remote.
@senamakel senamakel requested a review from a team July 15, 2026 04:41
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d88d7214-c9e6-444e-9e06-5b14ef0f54f9

📥 Commits

Reviewing files that changed from the base of the PR and between e8357ce and 3a54ef1.

📒 Files selected for processing (25)
  • app/src/hooks/useFlowChanged.ts
  • app/src/pages/FlowsPage.tsx
  • app/src/services/api/flowsApi.ts
  • docs/flows-agent-friendliness-audit.md
  • src/core/event_bus/events.rs
  • src/core/socketio.rs
  • src/openhuman/flows/agents/workflow_builder/agent.toml
  • src/openhuman/flows/agents/workflow_builder/prompt.md
  • src/openhuman/flows/builder_tools.rs
  • src/openhuman/flows/builder_tools_tests.rs
  • src/openhuman/flows/draft_store.rs
  • src/openhuman/flows/mod.rs
  • src/openhuman/flows/node_contracts.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_tests.rs
  • src/openhuman/flows/types.rs
  • src/openhuman/skills/tools.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs
  • src/openhuman/tools/user_filter.rs
  • vendor/tinyflows

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: 3a54ef140a

ℹ️ 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 +111 to +119
useFlowChanged(
useCallback(() => {
log('flow:changed — refetching list');
void listFlows()
.then(setFlows)
.catch(err => log('refetch failed: %o', err));
}, [])
);

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 Wire flow changes into the canvas save path

This subscription only refreshes the workflows list, but the stale-clobber scenario called out in the new hook still applies on /flows/:id: repo-wide search shows no useFlowChanged usage in FlowCanvasPage, and its save path still calls updateFlow(flowId, { graph: next }) without an expectedVersion. If an agent calls save_workflow while a user has the canvas open, the canvas keeps the old graph and the next Save overwrites the agent's update instead of refetching or surfacing a conflict. Please subscribe the canvas for its flowId and/or pass the loaded updated_at as the expected version before relying on these safety rails.

Useful? React with 👍 / 👎.

Comment on lines +293 to +300
Box::new(EditWorkflowTool::new(config.clone())),
// Standalone validate (F3): run the SAME structural + hard-gate stack
// the propose/save tools use, without emitting a proposal — a pure
// check so the agent can self-verify a draft mid-build. Read-only.
Box::new(ValidateWorkflowTool::new(config.clone())),
// Read a saved flow's revision history (F6) — prior graph snapshots the
// agent can inspect / pick a rollback target from. Read-only.
Box::new(GetFlowHistoryTool::new(config.clone())),

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 Classify new flow tools as Flows-domain tools

These newly registered flow-only tools are not added to the FLOWS list in tool_group below, so any custom DomainSet that keeps Platform enabled while disabling Flows will retain them because unknown names default to Platform. That bypasses the runtime domain gate for the new read/write/execute flow operations (edit_workflow, validate_workflow, get_flow_history, list_flow_runs, resume_flow_run, cancel_flow_run, create_workflow, duplicate_flow, etc.) even though the Flows controllers are supposed to be absent. Add the new names to the Flows classification or give them a prefix that maps to DomainGroup::Flows.

Useful? React with 👍 / 👎.

Comment on lines +564 to +565
const result = unwrapCliEnvelope<{ revisions: FlowRevision[] }>(response);
return result.revisions ?? [];

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 Align the history client with the RPC result shape

openhuman.flows_get_history returns RpcOutcome<Vec<FlowRevision>>, so after unwrapCliEnvelope the value is the revisions array itself, not an object with a revisions property. As written, any caller of this new helper gets [] for every non-empty history, which breaks the rollback/history safety rail once the UI starts using it. Unwrap FlowRevision[] directly or change the RPC to return { revisions }.

Useful? React with 👍 / 👎.

@senamakel senamakel merged commit f9eb90c into tinyhumansai:main Jul 15, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant