fix(desktop): align workflow read/save commands to the frontend contract#820
Merged
Conversation
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>
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_workflowsreturned{ workflows: [...] }with snake fields (yaml_definition, …); the frontend wrapper doesraw.map(...)on a bareRawWorkflow[]→ TypeError → the page error.get_workflowreturned the same wrapped/mismatched shape.create_workflow/update_workflowreturned{ workflow_id, event_id }; the frontend expects a full save record + optionalwebhook_secret.Fix (Rust-only; matches the existing frontend contract)
WorkflowWire/WorkflowSaveWirethat serialize the exact snake_case keysRawWorkflow/RawWorkflowSaveResponsedeclare.definitionobject; derivename(fallback to id); stablestatus: "active"(a validWorkflowStatus).get_channel_workflows/get_workflowreturn a bare array / bare object; create/update return a flat save record.definitionrather than failing the whole list.get_workflow_runs/get_run_approvalsnow 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 richRawWorkflowRun/RawWorkflowApprovalfields. 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 randesktop-tauri-testanddesktop-tauri-clippygreen.Known limitations / follow-ups (NOT this PR)
WorkflowRunRecordis not yet exposed to the desktop client as a queryable record).create_workflowid divergence — returns the relay DBworkflow_id, while the read path uses the client-generated kind:30620 d-tag asid. 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