feat(flows): agent-friendliness — editing, drafts, safety rails, wider tools#4876
Conversation
…ser, workflow-chat tagging
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.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 24 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (25)
Comment |
There was a problem hiding this comment.
💡 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".
| useFlowChanged( | ||
| useCallback(() => { | ||
| log('flow:changed — refetching list'); | ||
| void listFlows() | ||
| .then(setFlows) | ||
| .catch(err => log('refetch failed: %o', err)); | ||
| }, []) | ||
| ); | ||
|
|
There was a problem hiding this comment.
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 👍 / 👎.
| 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())), |
There was a problem hiding this comment.
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 👍 / 👎.
| const result = unwrapCliEnvelope<{ revisions: FlowRevision[] }>(response); | ||
| return result.revisions ?? []; |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
Makes the Flows (Workflows) product agent-friendly, per
docs/flows-agent-friendliness-audit.md, in six phased slices (reviewable commit-by-commit):validate_all; a queryable node-kind catalog (list_node_kinds/get_node_kind_contract);edit_workflowgraph patch-ops (no more whole-graph re-emits); one canonical gate runner +validate_workflow+ opt-instrictcreate/update.FlowDraftJSON files shared by the agent tools and canvas (flows_draft_*+promote);edit_workflow/dry_rundraft_idmode.expected_version→ structuredversion_conflict);FlowChanged→flow:changedsocket +useFlowChanged; revision history +flows_rollback+get_flow_history.list_flow_runs/resume_flow_run/cancel_flow_run; gatedcreate_workflow(born disabled) +duplicate_flow;dry_runallowed on read-only.flows_search_tool_catalog/get_tool_contractRPCs,required_connectionson the proposal + RPC,list_connectable_toolkits.create_workflow→create_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
tinyflowssubmodule (host-agnostic —validate_all, the node-kindcatalog,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.require_approvalfloor stays uncloseable, andpropose → user accepts → saveremains the default chat flow.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-sideworkflow_copilotthread tagging,run_flowdraft_id+ a real read-scopetest_runmode, and the broaderlist_workflows/run_workflow→*_skillrename (special-cased across the harness, so it warrants its own PR).Submission Checklist
validate/catalog/graph_ops) and the host (ops_tests,builder_tools_tests,draft_store,schemas), each covering success + failure/edge paths.docs/flows-agent-friendliness-audit.md(committed on this branch).Impact
flows_*RPCs + agent tools; frontend (desktop/web) — additiveflowsApimethods, auseFlowChangedsocket hook, and a silent FlowsPage refetch. No mobile-specific changes.flow_revisionsSQLite table (created idempotently, no migration of existing rows); drafts are plain JSON files under{workspace}/flows/drafts/(no schema). Optimistic concurrency reusesupdated_atas an etag — no new column.create_workflow/duplicate_flow/cancel_flow_run= Write,resume_flow_run= Execute + approval);dry_rundowngraded toNonebecause it is mock-only and side-effect-free.flows_search_tool_catalog/get_tool_contractare secret-free.FlowValidation.error_detailsis additive (serde(default)); the legacy skillscreate_workflow→create_skillrename fixes a duplicate-tool-name registry collision.Related
docs/flows-agent-friendliness-audit.mdon this branch.f182389).workflow_copilotthread tagging;run_flowdraft_id +test_run; thelist_workflows/run_workflow→*_skillrename.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:check— Prettier auto-fixes applied + committed (pre-push hook)pnpm typecheck— cleancargo test --lib openhuman::flows(327 pass; 1 known pre-existing LIVE_CATALOG_CACHE flake, passes in isolation), tinyflows cratecargo test --features mock(full suite green)cargo fmtapplied;cargo check --bin openhuman-corecleanapp/src-taurinot touchedValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
validate()still returns the same first error; propose/revise proposal contract unchanged;flows_create/flows_updatelast-write-wins by default (strict/expected_version are opt-in); enable/disable stays human-only.create_workflow→create_skillrename;dry_runstill mock-only.Duplicate / Superseded PR Handling