Files
lda-wf/docs/superpowers/specs/2026-05-31-workflow-surface-next-actions-design.md
T

6.8 KiB

Workflow Surface Next Actions Design

Purpose

next_actions is becoming a reusable UX pattern for MCP-facing workflow tools. It gives LLM clients a small, machine-readable answer to:

What should I call next?

This is especially useful when a client cannot easily read resources/prompts or when the MCP tool schema is technically correct but easy to misuse.

Boundary

next_actions is guidance, not authority.

  • Diagnostics describe machine-readable facts about validity, drift, source liveness, blocked resume, and runtime failures.
  • next_actions explains the likely next useful tool call.
  • Runtime validation, artifact validation, and dependency validation remain the source of truth.
  • next_actions.can_save_now is advisory only. It must not block saving.

Proposed Module

Create a focused module:

src/wf_mcp/workflow_surface/next_actions.py

It should own the reusable result models and constructors. This keeps handlers.py from accumulating UX policy helpers and keeps models.py focused on MCP request/response schemas.

Core Types

from __future__ import annotations

from enum import StrEnum
from typing import Any, Self

from pydantic import BaseModel, Field


class NextActionTool(StrEnum):
    """Stable MCP workflow tools that guidance may recommend."""

    PATCH_DRAFT_WORKSPACE = "wf.workflow.patch_draft_workspace"
    VALIDATE_DRAFT_WORKSPACE = "wf.workflow.validate_draft_workspace"
    VALIDATE_DEPLOYMENT = "wf.workflow.validate_deployment"
    RUN_DEPLOYMENT = "wf.workflow.run_deployment"
    RESUME_RUN = "wf.workflow.resume_run"
    READ_RUN_TRACE = "wf.workflow.read_run_trace"


class NextActionPatchExample(BaseModel):
    """Concrete example request for the recommended tool."""

    description: str
    tool: NextActionTool
    request: dict[str, Any]


class NextActions(BaseModel):
    """Advisory continuation hints for MCP workflow clients."""

    can_continue: bool = Field(
        description=(
            "Whether there is an obvious next workflow-surface tool call. "
            "Advisory only."
        )
    )
    can_save_now: bool | None = Field(
        default=None,
        description=(
            "Advisory wrapper-authoring signal. False means review is "
            "recommended before saving; the server does not enforce it."
        ),
    )
    recommended_next_tool: NextActionTool | None = Field(
        default=None,
        description="Suggested next MCP workflow tool, if one is obvious.",
    )
    reason: str
    patch_examples: list[NextActionPatchExample] = Field(default_factory=list)
    warnings: list[str] = Field(default_factory=list)

Constructors

Prefer named constructors or classmethods over ad-hoc dict helpers.

Initial constructor:

@classmethod
def from_wrapper_hints(
    cls,
    *,
    workspace_id: str,
    revision: int,
    hints: WrapperAuthoringHints | dict[str, Any],
) -> Self:
    """Create guidance after create_draft_workspace_from_capability."""

Later constructors:

@classmethod
def from_deployment_validation(
    cls,
    *,
    deployment_id: str,
    diagnostics: list[DependencyDiagnostic],
) -> Self:
    """Create guidance after validate_deployment."""


@classmethod
def from_run_result(
    cls,
    *,
    run_id: str | None,
    status: str,
    trace_count: int,
    diagnostics: list[DependencyDiagnostic],
) -> Self:
    """Create guidance after run_deployment, inspect_run, or resume_run."""

Wrapper Draft Guidance Rules

For create_draft_workspace_from_capability:

  • High confidence and no missing decisions:
    • can_continue=true
    • can_save_now=true
    • recommended_next_tool=wf.workflow.validate_draft_workspace
  • Low/medium confidence or any missing decisions:
    • can_continue=true
    • can_save_now=false
    • recommended_next_tool=wf.workflow.patch_draft_workspace
    • include conservative patch_examples when the missing decision can be represented safely

Patch examples must not invent business semantics.

Allowed examples:

  • replace step output bindings with an empty list as a scaffold
  • point the caller to patch_draft_workspace with the correct workspace_id and revision
  • include an empty patch for boolean-outcome decisions when no safe automatic route exists

Not allowed:

  • auto-extract content[0].text
  • auto-route on boolean output fields
  • infer error outcome mapping from arbitrary output data

Future Deployment Guidance

For validate_deployment:

  • No diagnostics:
    • recommend wf.workflow.run_deployment
  • source_unreachable:
    • recommend fixing/reloading source, then wf.workflow.validate_deployment with live_check=true
  • binding_missing:
    • recommend wf.workflow.save_deployment
  • capability_missing or schema_changed:
    • recommend inspecting capabilities or refreshing catalog before running

The exact deployment constructor can come later. The important rule is that diagnostics stay the source of truth; next_actions only summarizes.

Future Run Guidance

For run_deployment, inspect_run, and resume_run:

  • status=completed:
    • no required next tool
    • optionally suggest read_run_trace only if caller is debugging
  • status=failed:
    • recommend inspect_run or bounded read_run_trace
  • status=interrupted:
    • recommend wf.workflow.resume_run
  • blocked resume:
    • recommend repairing diagnostics and retrying resume_run

Never recommend reading the full trace. Always point to bounded trace ranges.

JSON Compatibility

Existing create_draft_workspace_from_capability output should remain stable:

{
  "next_actions": {
    "can_save_now": false,
    "recommended_next_tool": "wf.workflow.patch_draft_workspace",
    "reason": "...",
    "patch_examples": [],
    "warnings": []
  }
}

If the generic model adds can_continue, it is additive. Existing clients that only read can_save_now, recommended_next_tool, reason, patch_examples, and warnings continue to work.

Migration Plan

  1. Add next_actions.py with generic models and from_wrapper_hints.
  2. Re-export or import these models from workflow_surface.models if needed for FastMCP schema generation.
  3. Replace handler-local _wrapper_draft_next_actions helpers with the model constructor.
  4. Keep response JSON fields stable.
  5. Add tests proving the old next_actions fields still serialize the same.
  6. Later, add deployment/run constructors in separate small passes.

Open Questions

  • Should recommended_next_tool allow non-workflow tools such as wf.admin.reload_config, or should guidance stay workflow-surface only?
  • Should patch examples grow a typed tool_request_schema later, or is raw JSON request payload enough?
  • Should can_continue=false appear when there is no useful next tool, or should recommended_next_tool=null be enough?