---
phase: 04-foundation-types-and-contracts
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - crates/pmcp-tasks/src/types/workflow.rs
  - crates/pmcp-tasks/src/types/mod.rs
  - crates/pmcp-tasks/src/router.rs
  - crates/pmcp-tasks/src/lib.rs
  - src/server/tasks.rs
  - src/types/protocol.rs
  - src/server/workflow/prompt_handler.rs
  - src/server/mod.rs
  - src/server/core_tests.rs
  - src/server/dynamic.rs
  - crates/mcp-tester/src/tester.rs
  - examples/06_server_prompts.rs
  - examples/12_prompt_workflow_progress.rs
  - examples/17_completable_prompts.rs
  - examples/26-server-tester/src/tester.rs
  - examples/27-course-server-minimal/src/main.rs
  - cargo-pmcp/src/templates/complete_calculator.rs
  - tests/typescript_interop.rs
autonomous: true
requirements: [FNDX-02, FNDX-03, FNDX-04]

must_haves:
  truths:
    - "WorkflowProgress serializes to JSON with camelCase fields and deserializes back without data loss"
    - "StepStatus enum covers all runtime states (Pending, Completed, Failed, Skipped) without a pre-classification enum"
    - "TaskRouter trait has 3 new workflow methods with default error implementations"
    - "Existing TaskRouterImpl and all existing code compiles without modification (beyond struct literal field additions)"
    - "GetPromptResult now carries optional _meta field that serializes only when present"
    - "All existing tests pass without modification"
  artifacts:
    - path: "crates/pmcp-tasks/src/types/workflow.rs"
      provides: "WorkflowProgress, WorkflowStepProgress, StepStatus types"
      contains: "pub struct WorkflowProgress"
    - path: "src/server/tasks.rs"
      provides: "3 new default methods on TaskRouter trait"
      contains: "create_workflow_task"
    - path: "src/types/protocol.rs"
      provides: "_meta field on GetPromptResult"
      contains: "_meta"
    - path: "crates/pmcp-tasks/src/router.rs"
      provides: "3 new TaskRouterImpl methods implementing workflow task operations"
      contains: "create_workflow_task"
  key_links:
    - from: "crates/pmcp-tasks/src/types/workflow.rs"
      to: "crates/pmcp-tasks/src/router.rs"
      via: "WorkflowProgress deserialized from Value in router methods"
      pattern: "serde_json::from_value.*WorkflowProgress"
    - from: "src/server/tasks.rs"
      to: "crates/pmcp-tasks/src/router.rs"
      via: "TaskRouter trait implemented by TaskRouterImpl"
      pattern: "impl TaskRouter for TaskRouterImpl"
    - from: "src/types/protocol.rs"
      to: "src/server/workflow/prompt_handler.rs"
      via: "GetPromptResult struct literal construction includes _meta: None"
      pattern: "_meta: None"
---

<objective>
Define the foundation type contracts for the task-prompt bridge: WorkflowProgress schema, TaskRouter trait extension with 3 workflow methods, and _meta on GetPromptResult.

Purpose: These types form the contract layer that all subsequent phases (execution engine, handoff, integration) build upon. They must be compilable, serializable, and non-breaking to existing code.

Output: New WorkflowProgress types in pmcp-tasks, extended TaskRouter trait and implementation, _meta field on GetPromptResult, all existing tests passing.
</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

# Prior phase SUMMARYs needed for understanding TaskRouter trait and builder patterns
@.planning/milestones/v1.0-phases/03-handler-middleware-and-server-integration/03-01-SUMMARY.md
@.planning/milestones/v1.0-phases/03-handler-middleware-and-server-integration/03-02-SUMMARY.md

# Key source files to reference
@crates/pmcp-tasks/src/types/task.rs
@crates/pmcp-tasks/src/types/mod.rs
@crates/pmcp-tasks/src/router.rs
@crates/pmcp-tasks/src/lib.rs
@src/server/tasks.rs
@src/types/protocol.rs
@src/server/workflow/prompt_handler.rs
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create WorkflowProgress types and variable key constants in pmcp-tasks</name>
  <files>
    crates/pmcp-tasks/src/types/workflow.rs
    crates/pmcp-tasks/src/types/mod.rs
    crates/pmcp-tasks/src/lib.rs
  </files>
  <action>
Create `crates/pmcp-tasks/src/types/workflow.rs` with these types:

1. **`WorkflowProgress`** struct with fields:
   - `goal: String` -- the workflow's overall goal description
   - `steps: Vec<WorkflowStepProgress>` -- ordered list of steps
   - `schema_version: u32` -- starting at 1, for forward compatibility
   - Derive: `Debug, Clone, Serialize, Deserialize, PartialEq`
   - Use `#[serde(rename_all = "camelCase")]`

2. **`WorkflowStepProgress`** struct with fields:
   - `name: String` -- step name (from WorkflowStep)
   - `tool: Option<String>` -- tool name (None for resource-only steps), with `#[serde(skip_serializing_if = "Option::is_none")]`
   - `status: StepStatus` -- current step status
   - Derive: `Debug, Clone, Serialize, Deserialize, PartialEq`
   - Use `#[serde(rename_all = "camelCase")]`

3. **`StepStatus`** enum with variants: `Pending`, `Completed`, `Failed`, `Skipped`
   - Derive: `Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize`
   - Use `#[serde(rename_all = "snake_case")]`
   - Implement `Default` returning `Pending`

4. **Constants** at the top of the file:
   - `pub const WORKFLOW_PROGRESS_KEY: &str = "_workflow.progress";`
   - `pub const WORKFLOW_RESULT_PREFIX: &str = "_workflow.result.";`
   - `pub fn workflow_result_key(step_name: &str) -> String { format!("{}{}", WORKFLOW_RESULT_PREFIX, step_name) }`

**IMPORTANT (FNDX-02 dropped):** Do NOT create a `StepExecution` enum. Per CONTEXT.md locked decision, steps are best-effort at runtime -- no pre-classification. The `StepStatus` enum tracks runtime outcome (what happened), not execution mode (what should happen).

Add `pub mod workflow;` to `crates/pmcp-tasks/src/types/mod.rs` and `pub use workflow::*;`.

Add re-exports in `crates/pmcp-tasks/src/lib.rs` if the types module is not already fully re-exported (check existing pattern).

Add unit tests in the same file (or a `#[cfg(test)]` module):
- Serialization round-trip for `WorkflowProgress` with mixed step statuses
- JSON shape verification (camelCase field names, snake_case enum values)
- Default `StepStatus` is `Pending`
- `workflow_result_key("validate")` returns `"_workflow.result.validate"`

**Property tests (CLAUDE.md mandate):** Add a proptest round-trip test for `WorkflowProgress`. Use `proptest` (already a dev-dependency in pmcp-tasks). Generate arbitrary `WorkflowProgress` values (arbitrary goal strings, 0-20 steps with arbitrary names, random StepStatus variants, schema_version 1..=10) and verify: `serde_json::from_value::<WorkflowProgress>(serde_json::to_value(&progress).unwrap()).unwrap() == progress`. This proves serde round-trip holds for all valid inputs. Place the proptest in a `#[cfg(test)] mod proptest_workflow` submodule within the same file. If proptest is not yet a dev-dependency, add it to `crates/pmcp-tasks/Cargo.toml`.

Follow the exact serde patterns from `crates/pmcp-tasks/src/types/task.rs` (the `_meta` field pattern with `serde_json::Map`).
  </action>
  <verify>
Run: `cargo test --package pmcp-tasks --lib types::workflow` -- all new unit tests pass.
Run: `cargo test --package pmcp-tasks proptest` -- property tests pass.
Run: `cargo check --package pmcp-tasks` -- no compilation errors.
Run: `cargo clippy --package pmcp-tasks -- -D warnings` -- zero clippy warnings.
  </verify>
  <done>
WorkflowProgress, WorkflowStepProgress, and StepStatus types exist in pmcp-tasks with serde round-trip tests passing (both unit and proptest). Constants for variable key naming are defined. No StepExecution enum exists (FNDX-02 dropped). All existing pmcp-tasks tests still pass. Property test proves serde round-trip for arbitrary WorkflowProgress values.
  </done>
</task>

<task type="auto">
  <name>Task 2: Extend TaskRouter trait with 3 workflow methods and implement in TaskRouterImpl</name>
  <files>
    src/server/tasks.rs
    crates/pmcp-tasks/src/router.rs
  </files>
  <action>
**In `src/server/tasks.rs`** (the TaskRouter trait definition in pmcp core):

Add 3 new methods to the `TaskRouter` trait with default error implementations. These go AFTER the existing 8 methods (`handle_task_call` through `task_capabilities`). All params/returns use `serde_json::Value` to avoid circular crate dependency (this is the established pattern from Phase 3).

```rust
/// Create a workflow-backed task. Returns CreateTaskResult as Value.
///
/// Called by TaskWorkflowPromptHandler when a task-aware workflow prompt
/// is invoked. The implementation creates a task with the workflow's
/// initial progress stored in task variables.
async fn create_workflow_task(
    &self,
    _workflow_name: &str,
    _owner_id: &str,
    _progress: Value,
) -> Result<Value> {
    Err(Error::internal("workflow tasks not supported by this router"))
}

/// Update task variables with workflow step results.
///
/// Called after each step completes to persist the step result
/// and updated progress to the task's variable store.
async fn set_task_variables(
    &self,
    _task_id: &str,
    _owner_id: &str,
    _variables: Value,
) -> Result<()> {
    Err(Error::internal("workflow tasks not supported by this router"))
}

/// Complete a workflow task with final result.
///
/// Called when all steps have been executed (or the workflow determines
/// completion). Sets the task status to Completed and stores final result.
async fn complete_workflow_task(
    &self,
    _task_id: &str,
    _owner_id: &str,
    _result: Value,
) -> Result<Value> {
    Err(Error::internal("workflow tasks not supported by this router"))
}
```

**In `crates/pmcp-tasks/src/router.rs`** (TaskRouterImpl):

Implement the 3 new methods on the existing `impl TaskRouter for TaskRouterImpl` block:

1. **`create_workflow_task`**: Create a new task using the store. The `workflow_name` becomes the task title. Set initial task variables from the `progress` Value (deserialize to check it's a valid JSON object, then store under the `_workflow.progress` key). Return `CreateTaskResult` as Value. Use the existing `TaskParams` / store patterns from the existing `handle_task_call` method as reference.

2. **`set_task_variables`**: Deserialize the `variables` Value as a `HashMap<String, Value>`. For each key-value pair, call the store's set_variable method (or the equivalent batch set). Follow the existing variable storage pattern in `handle_task_call`.

3. **`complete_workflow_task`**: Update the task status to `Completed` and store the `result` in task variables. Return the updated task as Value.

Add unit tests in the existing test module in `router.rs`:
- `create_workflow_task` creates a task with progress in variables
- `set_task_variables` updates variables on an existing task
- `complete_workflow_task` transitions task to completed state
- All methods return errors for invalid task IDs or unauthorized owners

**IMPORTANT:** The default implementations in the trait MUST return errors (not panic). This ensures existing `TaskRouterImpl` code compiles without modification until the new methods are explicitly called.
  </action>
  <verify>
Run: `cargo test --package pmcp --lib server::tasks` -- existing trait tests still pass.
Run: `cargo test --package pmcp-tasks` -- all tests pass including new router tests.
Run: `cargo check --workspace` -- entire workspace compiles.
Run: `cargo clippy --workspace -- -D warnings` -- zero clippy warnings across workspace.
  </verify>
  <done>
TaskRouter trait has 3 new methods with default error implementations. TaskRouterImpl provides concrete implementations using TaskStore. All existing code compiles without modification. New methods have unit tests covering happy path and error cases.
  </done>
</task>

<task type="auto">
  <name>Task 3: Add _meta field to GetPromptResult and update all struct literal sites</name>
  <files>
    src/types/protocol.rs
    src/server/workflow/prompt_handler.rs
    src/server/mod.rs
    src/server/core_tests.rs
    src/server/dynamic.rs
    crates/mcp-tester/src/tester.rs
    examples/06_server_prompts.rs
    examples/12_prompt_workflow_progress.rs
    examples/17_completable_prompts.rs
    examples/26-server-tester/src/tester.rs
    examples/27-course-server-minimal/src/main.rs
    cargo-pmcp/src/templates/complete_calculator.rs
    tests/typescript_interop.rs
  </files>
  <action>
**In `src/types/protocol.rs`** at the `GetPromptResult` struct (line ~654):

Add the `_meta` field following the exact pattern from `pmcp-tasks::Task` type:

```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPromptResult {
    /// Prompt description
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// Prompt messages
    pub messages: Vec<PromptMessage>,
    /// Optional metadata for task-aware workflows (PMCP extension).
    ///
    /// When a workflow prompt is backed by a task, this field contains
    /// task state information (task_id, status, step plan) that
    /// task-aware MCP clients can use for structured continuation.
    #[serde(rename = "_meta")]
    #[serde(skip_serializing_if = "Option::is_none")]
    #[allow(clippy::pub_underscore_fields)]
    pub _meta: Option<serde_json::Map<String, serde_json::Value>>,
}
```

**Update ALL struct literal construction sites across the entire workspace.**

The checker identified 21 struct literal sites across 12+ files. Add `_meta: None` to every `GetPromptResult { ... }` construction. Known locations:

| File | Sites |
|------|-------|
| src/server/workflow/prompt_handler.rs | 3 |
| src/server/mod.rs | 4 |
| src/server/core_tests.rs | 1 |
| src/server/dynamic.rs | 1 |
| crates/mcp-tester/src/tester.rs | 1 |
| examples/06_server_prompts.rs | 3 |
| examples/12_prompt_workflow_progress.rs | 1 |
| examples/17_completable_prompts.rs | 2 |
| examples/26-server-tester/src/tester.rs | 1 |
| examples/27-course-server-minimal/src/main.rs | 2 |
| cargo-pmcp/src/templates/complete_calculator.rs | 1 |
| tests/typescript_interop.rs | 1 |

**IMPORTANT:** Do NOT rely solely on this list. After adding the field to the struct, run `cargo check --workspace` to find ALL compilation errors from the missing field. Fix every site. The list above is a starting point; there may be additional sites not yet identified.

This is a non-behavioral change -- adding a None-valued optional field does not change any execution flow or message content.

**NOTE on prompt_handler.rs:** The ONLY modification to `prompt_handler.rs` is adding `_meta: None` to its struct literals. The zero-diff constraint from CONTEXT.md is interpreted as behavior-identical, not file-content-identical. Adding `_meta: None` is mechanical and non-behavioral.

Add a unit test in `protocol.rs` (or nearby test module):
- `GetPromptResult` without `_meta` serializes to JSON without the `_meta` key (skip_serializing_if)
- `GetPromptResult` with `_meta` serializes and includes the `_meta` key
- Deserialization of JSON without `_meta` key produces `None` (backward compat)
  </action>
  <verify>
Run: `cargo test --package pmcp` -- all existing tests pass (the _meta: None additions are non-breaking).
Run: `cargo test --package pmcp --lib types` -- new _meta serialization tests pass.
Run: `cargo check --workspace` -- entire workspace compiles (confirms all struct literal sites updated).
Run: `cargo clippy --workspace -- -D warnings` -- zero warnings.
Run: `cargo test --workspace` -- full test suite passes.
  </verify>
  <done>
GetPromptResult has `_meta: Option<Map<String, Value>>` field. All 21+ struct literal sites across 12+ files updated with `_meta: None`. Serialization skips the field when None. Deserialization tolerates missing field. All existing tests pass unchanged (backward compatibility proven). prompt_handler.rs behavior is identical to v1.0.
  </done>
</task>

</tasks>

<verification>
1. `cargo test --workspace` -- all tests pass (existing + new)
2. `cargo clippy --workspace -- -D warnings` -- zero warnings
3. `cargo check --workspace` -- clean compilation
4. `cargo test --package pmcp-tasks proptest` -- property tests pass for WorkflowProgress round-trip
5. Verify `prompt_handler.rs` has NO behavioral changes (only `_meta: None` field additions)
6. Verify no `StepExecution` enum exists anywhere (FNDX-02 dropped)
</verification>

<success_criteria>
- WorkflowProgress, WorkflowStepProgress, StepStatus types compile and round-trip through serde
- Property test proves serde round-trip for arbitrary WorkflowProgress inputs
- TaskRouter trait has 3 new default-error methods; TaskRouterImpl has concrete implementations
- GetPromptResult carries optional _meta field
- All existing workflow tests pass without modification
- Zero clippy warnings across workspace
- FNDX-02 addressed by runtime behavior (no StepExecution enum)
</success_criteria>

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