---
name: workflow-manager
description: "Create, test, and diagnose persistent workflows that automate recurring multi-step processes spanning different plugin tools. Use when the user describes a recurring multi-step process to automate."
---

# Workflow Manager

Invoked from `specialists:project-manager`.

Create, test, and diagnose persistent workflows that automate recurring multi-step processes.

## When to Suggest a Workflow

The user describes a recurring process with two or more steps that involve different plugin tools. Examples: "every time a new contact signs up, send them a welcome message and schedule a follow-up", "when I get a new lead, create a contact, log it, and send a Telegram notification".

Do not suggest workflows for single-tool operations or one-off tasks.

## Gathering Requirements

Ask the user conversationally:

1. **What triggers this workflow?** Manual execution, scheduled (cron via scheduling plugin), or conversational ("run the follow-up workflow").
2. **What are the steps?** Each step is a single MCP tool call on a specific plugin. Identify the plugin name and tool name for each step.
3. **How do steps connect?** Which steps depend on output from earlier steps? Identify the `outputKey` for steps that produce data and the `{{outputKey.field}}` template variables for steps that consume it.
4. **What happens on failure?** For each step: abort the workflow, skip and continue, or retry (up to 3 times)?
5. **Any slow steps?** Steps calling tools that may take longer than 30 seconds (knowledge ingestion, external API calls) need an explicit `timeout` value (max 120 seconds).

## Presenting for Review

Before creating, show the user the planned workflow structure:

- Workflow name and description
- Each step numbered, showing: label, plugin/tool, parameters (with template variables highlighted), failure policy, and timeout if non-default
- Dependency chain (which steps must complete before which)
- Template variable flow (where data passes between steps)

Wait for the user's confirmation before calling `workflow-create`.

## Creating a Workflow

Use `workflow-create` with these parameters:
- `name` — short descriptive name (e.g. "New lead follow-up")
- `description` — what the workflow does, used for semantic discovery
- `steps` — array of step objects, each with:
  - `type`: `"tool"` (the only supported type)
  - `plugin`: plugin name (e.g. `"contacts"`, `"telegram"`, `"scheduling"`)
  - `tool`: tool name within the plugin (e.g. `"contact-create"`, `"message"`)
  - `params`: parameter object — use `{{outputKey.field}}` for values from prior steps
  - `label`: human-readable description
  - `dependsOn`: array of position indexes (as strings) for steps that must complete first — e.g. `["0"]` means "after step 0"
  - `onFailure`: `"abort"` (default), `"skip"`, or `"retry"`
  - `retryLimit`: 1-3, only when onFailure is `"retry"`
  - `outputKey`: name to store this step's output under
  - `timeout`: execution timeout in seconds (1-120, default 30)
- `inputs`: optional. The declared input contract (see Declared Inputs below).

If creation returns `status: "draft"`, tell the user which plugin capabilities are missing and that the workflow will activate automatically once those plugins are installed and built.

## Declared Inputs

A workflow declares the inputs it needs before it can run. The contract lives on the workflow definition, so the questions asked at run time come from the workflow, not from memory, and coverage is the same every run. This is the front half of gather then execute. The assistant resolves every variable choice with the operator at collection time, so by run time the context is flat and concrete and the linear steps are all that execution needs.

Each declared input has:

- `key`: the context key it provides, referenced as `{{key}}` in step params
- `prompt`: the question to ask the operator to gather this value
- `type`: `string`, `number`, `boolean`, or `object`
- `required`: whether execution is refused until the value is present
- `default`: optional value used when the operator does not supply one

When to declare an input: the workflow needs a value that is specific to each run and is not produced by an earlier step. A customer name, a line item, or a pricing option are declared inputs. A value a step produces is passed with `{{outputKey.field}}` and is not a declared input.

Capturing the contract on create: when steps reference `{{key}}` values that no earlier step produces, declare each one. Ask the operator what each value means and whether it has a sensible default, then pass the `inputs` array to `workflow-create` alongside the steps.

Gathering inputs on run: before calling `workflow-execute`, read the contract with `workflow-get`. Ask the operator each declared input's `prompt`. Where an input has a default, offer the default and let the operator accept or override it, then carry the concrete value forward. Build a `context` object keyed by each input's `key` and call `workflow-execute` with that complete context.

Handling an incomplete result: if `workflow-execute` returns `status: "incomplete"`, it ran no steps and lists the missing keys. Gather those specific values from the operator and call `workflow-execute` again with the complete context. Do not invent a value to satisfy the gate. A fabricated value passes the gate silently and produces a wrong run.

## Testing a Workflow

After creation, verify the workflow works:

1. Use `workflow-execute` with a test `context` object that includes a value for every required declared input, plus sample values for any other runtime variables the workflow expects.
2. Check the result: `status`, `stepsCompleted`, `stepsSkipped`, `stepsFailed`. A `status: "incomplete"` means a required input was missing and no steps ran; add the listed keys to the context and run again.
3. If any steps failed, use `workflow-runs` to get the detailed run history, then examine per-step results to identify which step failed and why.

## Diagnosing Failures

When the user reports a workflow isn't working:

1. Use `workflow-get` with the `workflowId` to check the workflow's current status and step configuration.
2. If status is `"draft"` — missing plugins. Use `workflow-validate` to see which capabilities are unavailable.
3. If status is `"paused"` — the user or the system paused it. Use `workflow-update` with `status: "active"` to resume (this re-validates capabilities first).
4. If status is `"active"` but execution fails — use `workflow-runs` to find recent runs, then examine per-step errors:
   - Template variable errors: a step references `{{outputKey.field}}` but the prior step didn't produce that output key or field
   - Tool errors: the underlying MCP tool returned an error (check the step result's error message)
   - Timeout errors: the step exceeded its timeout — consider increasing the `timeout` field via `workflow-update`

## Managing Existing Workflows

- **List**: `workflow-list` — optionally filter by `status` (`"active"`, `"paused"`, `"draft"`)
- **View details**: `workflow-get` with `workflowId`
- **Edit**: `workflow-update` — can change `name`, `description`, `steps` (full replacement), `inputs` (full replacement), or `status`
- **Pause/resume**: `workflow-update` with `status: "paused"` or `status: "active"`
- **Re-validate**: `workflow-validate` — checks if draft workflows can now activate after plugin changes
- **Delete**: `workflow-delete` — cascades to all steps, runs, and step results. Confirm with the user first.
- **Run history**: `workflow-runs` — optionally filter by `workflowId` or `status`

## Schedule Integration

To run a workflow on a schedule, create a scheduling event with action dispatch:
- Plugin: `scheduling`
- Tool: `schedule-event`
- Action: `{ plugin: "workflows", tool: "workflow-execute", args: { workflowId: "..." } }`

The `check-due-events` dispatcher (armed by the installer's once-a-minute heartbeat cron, see `maxy-code-prd.md` §Scheduled tasks) is the surface that fires the workflow at the scheduled time. The WorkflowRun captures `trigger: "schedule"` when the dispatcher fires.
