---
phase: 04-foundation-types-and-contracts
plan: 02
type: execute
wave: 2
depends_on: ["04-01"]
files_modified:
  - src/server/workflow/task_prompt_handler.rs
  - src/server/workflow/mod.rs
  - src/server/workflow/sequential.rs
  - src/server/builder.rs
  - examples/61_task_workflow_opt_in.rs
autonomous: true
requirements: [FNDX-01, FNDX-05]

must_haves:
  truths:
    - "TaskWorkflowPromptHandler exists, composes with WorkflowPromptHandler via delegation, and implements PromptHandler"
    - "Existing workflows without task support return identical GetPromptResult.messages to v1.0 (task opt-out path is a no-op)"
    - "SequentialWorkflow has an opt-in mechanism for task support (.with_task_support(true) or equivalent)"
    - "A workflow prompt with task support enabled creates a task when get_prompt is called"
    - "Existing workflows without task support work identically to v1.0"
    - "Builder correctly wraps opted-in workflows in TaskWorkflowPromptHandler when task_router is configured"
    - "A minimal example demonstrates the .with_task_support(true) API and compiles"
  artifacts:
    - path: "src/server/workflow/task_prompt_handler.rs"
      provides: "TaskWorkflowPromptHandler struct implementing PromptHandler"
      contains: "pub struct TaskWorkflowPromptHandler"
    - path: "src/server/workflow/mod.rs"
      provides: "Re-export of TaskWorkflowPromptHandler"
      contains: "task_prompt_handler"
    - path: "src/server/workflow/sequential.rs"
      provides: "Task support opt-in on SequentialWorkflow"
      contains: "task_support"
    - path: "src/server/builder.rs"
      provides: "Builder logic to wrap opted-in workflows in TaskWorkflowPromptHandler"
      contains: "TaskWorkflowPromptHandler"
    - path: "examples/61_task_workflow_opt_in.rs"
      provides: "Minimal example demonstrating task workflow opt-in API"
      contains: "with_task_support"
  key_links:
    - from: "src/server/workflow/task_prompt_handler.rs"
      to: "src/server/workflow/prompt_handler.rs"
      via: "Delegation -- TaskWorkflowPromptHandler holds a WorkflowPromptHandler and calls its get_prompt"
      pattern: "inner.*get_prompt"
    - from: "src/server/workflow/task_prompt_handler.rs"
      to: "src/server/tasks.rs"
      via: "TaskRouter trait methods called for task creation and variable updates"
      pattern: "task_router.*create_workflow_task"
    - from: "src/server/builder.rs"
      to: "src/server/workflow/task_prompt_handler.rs"
      via: "Builder wraps SequentialWorkflow in TaskWorkflowPromptHandler when task_support is true and task_router is configured"
      pattern: "TaskWorkflowPromptHandler"
    - from: "src/server/workflow/sequential.rs"
      to: "src/server/workflow/task_prompt_handler.rs"
      via: "task_support flag on SequentialWorkflow triggers wrapping in builder"
      pattern: "task_support"
---

<objective>
Create the TaskWorkflowPromptHandler composition layer and the opt-in mechanism that binds task-aware workflows to the task lifecycle without modifying the existing WorkflowPromptHandler.

Purpose: This establishes the composition boundary (FNDX-05) and the task creation path (FNDX-01). When a workflow opts in to task support and the server has a task router configured, invoking the workflow prompt creates a task, delegates execution to the existing handler, then enriches the result with task state in _meta.

Output: TaskWorkflowPromptHandler struct, opt-in on SequentialWorkflow, builder wiring, minimal example, backward compatibility proven.
</objective>

<execution_context>
@./.claude/get-shit-done/workflows/execute-plan.md
@./.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-foundation-types-and-contracts/04-CONTEXT.md
@.planning/phases/04-foundation-types-and-contracts/04-RESEARCH.md

# Plan 01 summary needed -- uses types and TaskRouter methods created there
@.planning/phases/04-foundation-types-and-contracts/04-01-SUMMARY.md

# Key source files
@src/server/workflow/prompt_handler.rs
@src/server/workflow/mod.rs
@src/server/workflow/sequential.rs
@src/server/builder.rs
@src/server/tasks.rs
@crates/pmcp-tasks/src/types/workflow.rs
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create TaskWorkflowPromptHandler with delegation composition</name>
  <files>
    src/server/workflow/task_prompt_handler.rs
    src/server/workflow/mod.rs
  </files>
  <action>
Create `src/server/workflow/task_prompt_handler.rs` containing `TaskWorkflowPromptHandler`.

**Struct definition:**
```rust
pub struct TaskWorkflowPromptHandler {
    /// The inner workflow handler that does actual step execution
    inner: WorkflowPromptHandler,
    /// Task router for creating/managing workflow tasks
    task_router: Arc<dyn TaskRouter>,
    /// The workflow definition (needed for step metadata to build WorkflowProgress)
    workflow: SequentialWorkflow,
}
```

**Implement `PromptHandler` trait** (from `src/server/traits.rs`) for `TaskWorkflowPromptHandler`:

1. **`metadata(&self)`**: Delegate to `self.inner.metadata()` unchanged. The prompt name, description, and arguments are identical.

2. **`get_prompt(&self, args, extra)`**: This is the core composition method. To keep cognitive complexity below 25, extract the following private helper methods BEFORE writing `get_prompt`:

   **Helper: `fn build_initial_progress(&self) -> WorkflowProgress`**
   Iterates `self.workflow` steps, creates a `WorkflowStepProgress` for each with `status: StepStatus::Pending`, extracts tool name from each step. Returns a complete `WorkflowProgress` with `goal` from the workflow name/description and `schema_version: 1`.

   **Helper: `fn infer_step_statuses(messages: &[PromptMessage], steps: &[WorkflowStepProgress]) -> Vec<StepStatus>`**
   Scans the message list for tool-call/result pairs. Each pair corresponds to a sequential step. Steps beyond the last completed pair remain Pending. This is fragile by nature (depends on message structure), so it MUST have thorough unit test coverage (see done criteria).

   **Helper: `fn build_meta_map(task_id: &str, steps: &[WorkflowStepProgress]) -> serde_json::Map<String, Value>`**
   Creates the `_meta` map with: `"task_id"` (the task ID string), `"task_status"` ("working"), `"steps"` (brief array of `{name, status}` for each step).

   **With helpers extracted, `get_prompt` becomes a linear orchestration:**
   a. Resolve owner from `extra` (use the same pattern as ServerCore's `resolve_task_owner`).
   b. Call `self.build_initial_progress()` to get the initial WorkflowProgress.
   c. Create task via `self.task_router.create_workflow_task(...)`. Extract `task_id` from returned Value.
   d. Delegate to `self.inner.get_prompt(args, extra).await`. Do NOT modify this call.
   e. Call `infer_step_statuses(&result.messages, &progress.steps)` to determine outcomes.
   f. Update task variables via `task_router.set_task_variables()` with step results and updated progress.
   g. Call `build_meta_map(task_id, &updated_steps)` and set `result._meta = Some(meta_map)`.
   h. Return the enriched `GetPromptResult`.

**Error handling:** If task creation fails, log the error and fall back to returning the inner handler's result without _meta (graceful degradation -- the workflow still works, just without task tracking). Do NOT fail the entire prompt because of task infrastructure issues.

**Constructor:**
```rust
impl TaskWorkflowPromptHandler {
    pub fn new(
        inner: WorkflowPromptHandler,
        task_router: Arc<dyn TaskRouter>,
        workflow: SequentialWorkflow,
    ) -> Self {
        Self { inner, task_router, workflow }
    }
}
```

**Add module to `src/server/workflow/mod.rs`:**
- Add `pub mod task_prompt_handler;`
- Add `pub use task_prompt_handler::TaskWorkflowPromptHandler;`

**DO NOT modify `prompt_handler.rs`** in this task. The composition is purely additive via a new file.

Follow existing code conventions: `use async_trait::async_trait;`, proper error types, doc comments on public items.
  </action>
  <verify>
Run: `cargo check --package pmcp` -- compiles without errors.
Run: `cargo clippy --package pmcp -- -D warnings` -- zero warnings.
Run: `cargo test --package pmcp` -- all existing tests pass (new handler not yet wired, so no new test paths triggered).
Run: `cargo test --package pmcp --lib server::workflow::task_prompt_handler` -- step-status inference unit tests pass.
  </verify>
  <done>
TaskWorkflowPromptHandler struct exists in src/server/workflow/task_prompt_handler.rs, implements PromptHandler via delegation to WorkflowPromptHandler, creates a task on invocation, enriches result with _meta containing task state. Module is re-exported from workflow/mod.rs. WorkflowPromptHandler has zero new modifications. `get_prompt` delegates to 3 private helpers (`build_initial_progress`, `infer_step_statuses`, `build_meta_map`) keeping its cognitive complexity below 25. Unit test covering step-status inference: given a message list with N tool-call/result pairs, verify WorkflowProgress shows N Completed steps and remaining Pending steps. Edge cases tested: zero pairs (all Pending), all pairs present (all Completed), partial completion.
  </done>
</task>

<task type="auto">
  <name>Task 2: Add task support opt-in to SequentialWorkflow, wire in builder, and create minimal example</name>
  <files>
    src/server/workflow/sequential.rs
    src/server/builder.rs
    examples/61_task_workflow_opt_in.rs
  </files>
  <action>
**In `src/server/workflow/sequential.rs`:**

Add a `task_support` field to `SequentialWorkflow`:

```rust
/// Whether this workflow should be backed by a task for durable progress tracking.
/// When true and a task router is configured on the server, the workflow will be
/// wrapped in a TaskWorkflowPromptHandler that creates a task on invocation.
task_support: bool,
```

Add a builder method:
```rust
/// Enable task support for this workflow.
///
/// When task support is enabled and a task router is configured on the server
/// (via `ServerCoreBuilder::with_task_store()`), invoking this workflow prompt
/// will create a task and track step progress in task variables.
///
/// If task support is enabled but no task router is configured, the builder
/// will return an error at build time.
pub fn with_task_support(mut self, enabled: bool) -> Self {
    self.task_support = enabled;
    self
}
```

Add a public accessor:
```rust
/// Returns whether this workflow has task support enabled.
pub fn has_task_support(&self) -> bool {
    self.task_support
}
```

Initialize `task_support: false` in the `SequentialWorkflow::new()` constructor (or `Default` impl, whichever exists). This ensures all existing workflows default to no task support.

**In `src/server/builder.rs`:**

Find the `prompt_workflow()` method (or equivalent method that registers a SequentialWorkflow as a prompt handler). Modify it to check:

1. If the workflow has `task_support == true`:
   a. Check if `self.task_router` is `Some(...)` (a task store has been configured via `with_task_store()`)
   b. If both: Create a `WorkflowPromptHandler` as usual, then wrap it in a `TaskWorkflowPromptHandler::new(handler, task_router.clone(), workflow.clone())`. Register the `TaskWorkflowPromptHandler` as the prompt handler instead.
   c. If task_support is true but no task_router: Return an error (or panic with a clear message) at build time. The developer explicitly requested task support but forgot to configure the task store. This should fail fast.
2. If the workflow has `task_support == false`: Proceed exactly as before -- create a `WorkflowPromptHandler` and register it. Zero behavioral change.

**This ensures backward compatibility:** Any existing call to `prompt_workflow()` with a `SequentialWorkflow` that does NOT call `.with_task_support(true)` will work identically to v1.0. The new code path only activates when explicitly opted in.

**Minimal example (CLAUDE.md mandate):**

Create `examples/61_task_workflow_opt_in.rs` -- a minimal example that demonstrates the `.with_task_support(true)` API. This is NOT the full lifecycle example (that is Phase 7's `62_tasks_workflow.rs`). This example should:

1. Create a `SequentialWorkflow` with 2-3 simple steps and call `.with_task_support(true)`
2. Build a `ServerCore` with both `with_task_store()` and the task-enabled workflow registered via `prompt_workflow()`
3. Assert/print that the workflow was registered successfully (the builder did not error)
4. The example does NOT need to run the server or execute the workflow -- it just proves the wiring compiles and the opt-in API works end-to-end through the builder

Pattern: Follow the structure of existing examples (e.g., `examples/06_server_prompts.rs` or `examples/12_prompt_workflow_progress.rs`) for imports, main function structure, and error handling.

Add tests in the builder's test module:
- A workflow WITHOUT task support registers as `WorkflowPromptHandler` (existing behavior)
- A workflow WITH task support AND task router registers as `TaskWorkflowPromptHandler`
- A workflow WITH task support but NO task router produces a build error
  </action>
  <verify>
Run: `cargo test --package pmcp` -- all tests pass, including new builder tests.
Run: `cargo check --workspace` -- entire workspace compiles.
Run: `cargo check --example 61_task_workflow_opt_in` -- example compiles.
Run: `cargo clippy --workspace -- -D warnings` -- zero warnings.
Run: `cargo test --workspace` -- full suite passes.
Verify: Existing examples that use `prompt_workflow()` still compile and work (they don't call `.with_task_support(true)`, so they take the unchanged path).
  </verify>
  <done>
SequentialWorkflow has `.with_task_support(true)` builder method. Builder wraps opted-in workflows in TaskWorkflowPromptHandler when task router is configured. Existing workflows without task support work identically to v1.0. Build-time error when task support requested but no task router configured. Minimal example `61_task_workflow_opt_in.rs` compiles and demonstrates the opt-in API wiring through the builder.
  </done>
</task>

</tasks>

<verification>
1. `cargo test --workspace` -- all tests pass
2. `cargo clippy --workspace -- -D warnings` -- zero warnings
3. `cargo check --example 61_task_workflow_opt_in` -- example compiles
4. Verify `prompt_handler.rs` git diff shows ONLY `_meta: None` additions (from Plan 01), no other changes
5. Verify existing workflow examples compile and run unchanged
6. Verify `TaskWorkflowPromptHandler` creates a task and enriches _meta when invoked
7. Verify `SequentialWorkflow` without `.with_task_support(true)` behaves identically to v1.0
</verification>

<success_criteria>
- TaskWorkflowPromptHandler composes with WorkflowPromptHandler via delegation (FNDX-05)
- get_prompt cognitive complexity stays below 25 via helper extraction
- Workflow prompt with task support creates a task when invoked (FNDX-01)
- WorkflowPromptHandler has zero behavioral changes
- Builder correctly wires task support based on opt-in + router availability
- All existing tests and examples pass unchanged
- Step-status inference from message list has unit test coverage
- Minimal example demonstrates and compiles the opt-in API
</success_criteria>

<output>
After completion, create `.planning/phases/04-foundation-types-and-contracts/04-02-SUMMARY.md`
</output>
