Skip to content

fix(desktop): align workflow read/save commands to the frontend contract#820

Merged
wesbillman merged 2 commits into
mainfrom
brain/workflows-read-contract
Jun 2, 2026
Merged

fix(desktop): align workflow read/save commands to the frontend contract#820
wesbillman merged 2 commits into
mainfrom
brain/workflows-read-contract

Conversation

@wesbillman

Copy link
Copy Markdown
Collaborator

Problem

The desktop Workflows page showed "Failed to load workflows". PR #475 ("Sprout speaks Nostr") rewrote the Tauri workflow commands to query the relay directly, but they no longer matched the contract the frontend already declares in desktop/src/shared/api/tauriWorkflows.ts (RawWorkflow, RawWorkflowSaveResponse):

  • get_channel_workflows returned { workflows: [...] } with snake fields (yaml_definition, …); the frontend wrapper does raw.map(...) on a bare RawWorkflow[]TypeError → the page error.
  • get_workflow returned the same wrapped/mismatched shape.
  • create_workflow / update_workflow returned { workflow_id, event_id }; the frontend expects a full save record + optional webhook_secret.

Fix (Rust-only; matches the existing frontend contract)

  • Typed wire structs WorkflowWire / WorkflowSaveWire that serialize the exact snake_case keys RawWorkflow / RawWorkflowSaveResponse declare.
  • Parse the workflow YAML into a free-form definition object; derive name (fallback to id); stable status: "active" (a valid WorkflowStatus).
  • get_channel_workflows / get_workflow return a bare array / bare object; create/update return a flat save record.
  • A malformed workflow falls back to an empty definition rather than failing the whole list.
  • get_workflow_runs / get_run_approvals now return a bare empty [] instead of a { runs: [...] } wrapper of raw lifecycle events. The frontend maps over these on detail-panel open, so the wrapper would crash the panel with the same TypeError class; raw events also lack the rich RawWorkflowRun / RawWorkflowApproval fields. Reconstructing those records is a clearly-scoped follow-up (see below).

Tests

desktop/src-tauri/src/commands/workflows_tests.rs (11 tests): event→wire mapping, YAML parsing, name derivation + fallbacks, malformed-YAML safety, the serialized snake_case wire contract, and a regression test pinning runs/approvals to a bare [].

Verified: cargo test --lib workflows, cargo clippy --lib --tests -- -D warnings, pnpm typecheck — all green. The pre-push hooks additionally ran desktop-tauri-test and desktop-tauri-clippy green.

Known limitations / follow-ups (NOT this PR)

  1. Rich run/approval records — the detail panel's runs list is intentionally empty until run state is reconstructed (relay DB WorkflowRunRecord is not yet exposed to the desktop client as a queryable record).
  2. create_workflow id divergence — returns the relay DB workflow_id, while the read path uses the client-generated kind:30620 d-tag as id. The UI needs the DB id for trigger/webhook/delete. Pre-existing in Sprout speaks Nostr. Nothing else. #475's design; same family as (1).

A full live-relay + desktop click-through was not performed; the contract is unit-verified on both sides and statically traced end-to-end. Reviewed by @Pinky.

Co-authored-by: Brain 21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co

wesbillman and others added 2 commits June 2, 2026 12:40
The Workflows page errored ("Failed to load workflows") because PR #475
rewrote the Tauri workflow commands to query the relay directly, but they
never matched the contract `tauriWorkflows.ts` / `workflowTypes.ts` already
declare:

- get_channel_workflows returned `{ workflows: [...] }` (snake fields like
  `yaml_definition`); the frontend wrapper does `raw.map(...)` on a bare
  `RawWorkflow[]` -> TypeError -> the page error.
- get_workflow returned the same wrapped/mismatched shape.
- create/update_workflow returned `{ workflow_id, event_id }`; the frontend
  wants a full save record + optional `webhook_secret`.

Fix: introduce typed wire structs (`WorkflowWire`, `WorkflowSaveWire`) that
serialize the exact snake_case keys the frontend's `RawWorkflow` /
`RawWorkflowSaveResponse` declare, parse the workflow YAML into a free-form
`definition` object, derive `name` (fallback to id) and a stable
`status: "active"`, and return bare arrays/objects. A malformed workflow
falls back to an empty definition rather than failing the whole list.

get_workflow_runs / get_run_approvals now return a bare empty array instead
of a `{ runs: [...] }` wrapper of raw lifecycle events. The frontend maps
over them, so the wrapper would crash the detail panel with the same
TypeError class; raw events also lack the rich `WorkflowRun`/`WorkflowApproval`
fields. Reconstructing those records from the lifecycle-event stream is a
clearly-scoped follow-up.

Tests: pure helpers covered in workflows_tests.rs (event->wire mapping, YAML
parsing, name derivation/fallbacks, malformed-YAML safety, wire-key contract).

Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
…test

Address review nits on the workflow read-contract fix:

- Tag the get_workflow_runs / get_run_approvals placeholder comments with
  TODO(workflow-runs) so the empty-[] stopgap is findable and doesn't
  calcify into permanent behavior. Run/approval reconstruction from the
  relay's lifecycle-event stream remains a scoped follow-up.
- Add a regression test asserting both commands' return value serializes
  to a bare JSON array ("[]"), not a wrapped object. The frontend wrappers
  do raw.map(...), so a { runs: [...] } / { approvals: [...] } shape would
  reintroduce the TypeError that crashed the detail panel.

Tests (11) + clippy + tsc all green.

Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
@wesbillman wesbillman requested a review from a team as a code owner June 2, 2026 18:56
@wesbillman wesbillman merged commit b72eee3 into main Jun 2, 2026
15 checks passed
@wesbillman wesbillman deleted the brain/workflows-read-contract branch June 2, 2026 19:52
michaelneale added a commit that referenced this pull request Jun 4, 2026
* origin/main: (36 commits)
  fix: use immutable commit-SHA URLs in screenshot PR comments (#842)
  feat(mobile+desktop): two-tier Slack-style app icon badge (#802)
  chore: simplify file-size check to a flat 1000-line limit (#839)
  fix(desktop): robust emoji picker — unify picker + fix custom emoji in editing, status, reactions (#837)
  feat(desktop): reusable screenshot workflow for agents (#826)
  desktop(mesh-llm): let a serving node route a different model (#833)
  chore(release): release version 0.3.9 (#832)
  fix: native arbitrary-file download + image context-menu flash (#830)
  fix(desktop): custom emoji reaction rendering + picker autofocus (#831)
  Mesh-LLM v1: relay-gated direct-iroh inference between users (WAN) (#822)
  chore(release): release version 0.3.8 (#829)
  chore(release): release version 0.3.7 (#825)
  feat: code block rendering, syntax highlighting, and compose fixes (#803)
  feat: custom emoji — user-owned NIP-30 sets with a client-side union (#816)
  Install sprout-cli skill at repo root + fix desktop clippy (#818)
  fix(desktop): use public re-export path for ensure_client_node_for_model (#824)
  refactor(desktop): feature-gate mesh-llm-sdk behind optional Cargo feature (#823)
  fix(desktop): align workflow read/save commands to the frontend contract (#820)
  fix(desktop): disable mesh-llm auto-build to prevent git config corruption (#819)
  fix(desktop): clear clippy lints in agents/mesh_llm commands (#817)
  ...

# Conflicts:
#	Cargo.lock
#	desktop/scripts/check-file-sizes.mjs
#	desktop/src-tauri/Cargo.toml
#	desktop/src/app/AppShell.tsx
#	desktop/src/app/AppTopChrome.tsx
#	desktop/src/features/messages/hooks.ts
#	desktop/src/features/workspaces/useWorkspaceInit.ts
#	desktop/src/shared/api/tauri.ts
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