Skip to content

Flow engine: fix loop routing, edge serialization, approval routing & permissions; add approvals inbox#397

Draft
esafwan wants to merge 1 commit into
developfrom
track/flow-handles
Draft

Flow engine: fix loop routing, edge serialization, approval routing & permissions; add approvals inbox#397
esafwan wants to merge 1 commit into
developfrom
track/flow-handles

Conversation

@esafwan

@esafwan esafwan commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the highest-priority defects in the Flow system (engine routing, builder serialization, approval routing, permissions) and adds a pending-approvals inbox so human-in-the-loop steps are discoverable.

Engine (huf/ai/flow_engine.py)

  • Loop nodes now loop. The execution dispatch previously ignored the loop executor's next_node_id, so loop_node/done_node config was dead and loops never iterated. The dispatch now routes body-vs-done from the executor; a loop with no done_node completes the run gracefully.
  • Rejection no longer routes like approval. A rejected approval with no meta.outcome: rejected edge used to fall back to the success-edge path (the same path as approval). It now fails the run with Approval rejected by <user>[: comment]. The success-edge fallback applies only to approved decisions.
  • approval_type spelling split fixed. Notifications matched only "users" while permission checks matched only "user", so UI-authored approvals got no notifications/inbox and API-authored ones got no enforcement. Both spellings are now accepted everywhere (notification fan-out, _verify_approval_permission, get_pending_approvals).

Validation (flow_definition.py)

  • trigger.schedule and trigger.doc-event added to ALLOWED_NODE_TYPES — these executors already exist and are advertised by get_node_schemas, but definitions using them could never be saved.

Permissions (flow_run.json, huf/ai/flow_api.py)

  • Huf Manager gains create on Flow Run (previously only System Manager could start flows via the API).
  • approve_flow_run now requires read + approver identity instead of write, so a named approver with the Huf User role can actually approve.
  • All four agent tool handlers (handle_run_flow, handle_get_flow_run, handle_resume_flow_run, handle_approve_flow_run) previously had no permission checks; they now mirror the REST endpoints, and the approve handler additionally verifies approver identity — an agent session can no longer self-approve unless its user is an authorized approver.
  • Webhook-triggered runs now execute as the flow owner instead of Guest (previously any agent/tool node inside a webhook flow failed unless the agent was guest-open).

Builder (frontend/src)

  • Edge settings now survive saving. The edge editor wrote data.type/data.expression while the serializer read data.edgeType/data.condition, so every UI-saved edge silently became always — no expression edges, no failure routing. Keys are aligned, and the editor gains Priority and Approval Outcome (meta.outcome: approved/rejected) controls so approve/reject branching is authorable.
  • Expression placeholder now shows the syntax the evaluator accepts (context["status"] == "approved") instead of mustache syntax it rejects.
  • Trigger types round-trip: schedule and doc-event triggers serialize as trigger.schedule/trigger.doc-event instead of being flattened to trigger.webhook (and deserialize back correctly, with labels/icons).

Approvals inbox (new)

  • ApprovalsBell component mounted in the unified header on every page: shows a badge with the pending-approval count for the current user (first consumer of the get_pending_approvals endpoint), refreshes on flow_paused/flow_completed/flow_error realtime events plus a 60-second poll, and offers inline Approve/Reject and an Open deep link to the run viewer.

What to test

Loop

  1. Build a flow: trigger → loop (list of 3 items, loop_node = a transform in the body, done_node = end) with a body→loop edge. Run it. Before: one pass then stop. Expect: 3 iterations then completion via done_node.
  2. Same loop without done_node: expect graceful Success after the last iteration.

Edges
3. In the builder, set an edge to type expression with context["x"] == 1, save, reload: type and condition must persist (before: reverted to always).
4. Add an on_failure edge from a node that fails (e.g. bad HTTP request): the failure branch must be taken after save/reload.
5. Set Priority on two competing edges and confirm the higher one wins.

Approvals
6. Flow with human.approval (approval_type: user, a named approver): the approver must receive a notification and see the item in the bell inbox (before: neither).
7. Author two outgoing edges with Approval Outcome approved and rejected; approve one run and reject another: each must follow its own branch.
8. Reject a run that has only a plain edge after the approval: run must end Failed with "Approval rejected by …" (before: continued down the approve path as Success).
9. Bell: badge count updates when a run pauses (realtime) and after approve/reject from the popover; Open link lands on the run viewer.
10. As a Huf User who is the named approver: approving via API must succeed. As an agent whose session user is not an approver, the approve_flow_run tool must be refused.

Triggers
11. Save a flow whose entry is a schedule trigger, reload: it must save (no validation error) and stay a schedule trigger (before: unsavable; if forced, flattened to webhook).

Permissions / webhook
12. As Huf Manager, start a flow via the run API: must succeed (before: PermissionError).
13. Trigger a webhook flow containing an agent.run node: it must execute as the flow owner and the agent node must run (before: Guest → agent refused).

Regression
14. Existing webhook-triggered flows, condition nodes, and the run viewer approve/reject buttons behave as before.
15. cd frontend && npm run typecheck passes; backend imports clean.

…oval routing/permissions; add approvals inbox bell

- Engine: honor loop executor next_node_id; reject decisions no longer
  follow approval-path edges; approval_type 'user'/'users' treated as
  synonyms in notifications, permission checks, and pending-approvals API
- Validation: allow trigger.schedule and trigger.doc-event node types
- Permissions: Huf Manager can create Flow Runs; approve endpoint requires
  read + approver identity; agent tool handlers enforce permissions and
  approver identity; webhook runs execute as flow owner instead of Guest
- Builder: edge editor writes edgeType/condition keys the serializer reads,
  adds priority and approval-outcome (meta.outcome) controls; trigger types
  round-trip through the serializer
- New ApprovalsBell in the unified header: pending-approvals inbox with
  badge, realtime + polled refresh, inline approve/reject, run deep link
@esafwan

esafwan commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Rebased version of this PR is open as draft #408: your head commit cherry-picked cleanly onto current develop, plus two follow-ups identified during intake — flow_failed is now forwarded in SocketContext.tsx (the bell would otherwise wait for the 60s poll on rejections) and the AGENTS.md Flow Engine section is updated. Bench-validated (migrate + smoke) on an isolated bench. Note the merge-order consideration vs #404 (both touch approve_flow_run).

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