---
phase: 05-partial-execution-engine
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - crates/pmcp-tasks/src/types/workflow.rs
  - src/server/workflow/workflow_step.rs
  - src/server/workflow/prompt_handler.rs
autonomous: true
requirements: [EXEC-01, EXEC-02, EXEC-03, EXEC-04]

must_haves:
  truths:
    - "PauseReason enum serializes to camelCase JSON with four variants matching CONTEXT.md decisions"
    - "WORKFLOW_PAUSE_REASON_KEY constant exists for storing pause reason in task variables"
    - "WorkflowStep has a retryable field (default false) that tool steps can opt into"
    - "ExecutionContext and all execution helper methods on WorkflowPromptHandler are pub(crate) visible"
    - "Existing WorkflowPromptHandler behavior is unchanged (zero behavioral modification)"
  artifacts:
    - path: "crates/pmcp-tasks/src/types/workflow.rs"
      provides: "PauseReason enum, WORKFLOW_PAUSE_REASON_KEY constant"
      contains: "pub enum PauseReason"
    - path: "src/server/workflow/workflow_step.rs"
      provides: "retryable field on WorkflowStep"
      contains: "retryable"
    - path: "src/server/workflow/prompt_handler.rs"
      provides: "pub(crate) visibility on ExecutionContext and helper methods"
      contains: "pub(crate)"
  key_links:
    - from: "crates/pmcp-tasks/src/types/workflow.rs"
      to: "PauseReason used by task_prompt_handler.rs in Plan 02"
      via: "pub use in types/mod.rs"
      pattern: "pub enum PauseReason"
    - from: "src/server/workflow/prompt_handler.rs"
      to: "task_prompt_handler.rs calls helpers in Plan 02"
      via: "pub(crate) method visibility"
      pattern: "pub\\(crate\\) (fn|struct)"
---

<objective>
Add PauseReason types, WorkflowStep retryable field, and open up WorkflowPromptHandler internals for composition.

Purpose: Phase 5's execution engine (Plan 02) needs typed pause reasons to report why execution stopped, a retryable hint per step, and access to WorkflowPromptHandler's execution helpers (resolve_tool_parameters, execute_tool_step, etc.) to run its own step loop. This plan provides those foundations without changing any runtime behavior.

Output: Updated workflow types in pmcp-tasks, retryable field on WorkflowStep, pub(crate) visibility on prompt_handler.rs internals.
</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/05-partial-execution-engine/05-CONTEXT.md
@.planning/phases/05-partial-execution-engine/05-RESEARCH.md
@crates/pmcp-tasks/src/types/workflow.rs
@src/server/workflow/prompt_handler.rs
@src/server/workflow/workflow_step.rs
@src/server/workflow/task_prompt_handler.rs
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add PauseReason enum and WORKFLOW_PAUSE_REASON_KEY to pmcp-tasks</name>
  <files>crates/pmcp-tasks/src/types/workflow.rs</files>
  <action>
Add to `crates/pmcp-tasks/src/types/workflow.rs`:

1. **WORKFLOW_PAUSE_REASON_KEY constant** — `pub const WORKFLOW_PAUSE_REASON_KEY: &str = "_workflow.pause_reason";`

2. **PauseReason enum** with `#[serde(tag = "type", rename_all = "camelCase")]` and four variants per CONTEXT.md locked decisions:
   - `UnresolvableParams { blocked_step: String, missing_param: String, suggested_tool: String }` — step can't resolve its parameters
   - `SchemaMismatch { blocked_step: String, missing_fields: Vec<String>, suggested_tool: String }` — resolved params don't satisfy tool schema
   - `ToolError { failed_step: String, error: String, retryable: bool, suggested_tool: String }` — tool execution returned an error
   - `UnresolvedDependency { blocked_step: String, missing_output: String, producing_step: String, suggested_tool: String }` — step depends on failed/skipped step

   All fields use `#[serde(rename_all = "camelCase")]` on the enum (already provided by the tag attribute with the rename). Each variant must have comprehensive rustdoc describing when it is produced and what the client should do.

3. **Unit tests** for PauseReason:
   - `pause_reason_unresolvable_params_serde_round_trip` — serialize + deserialize UnresolvableParams
   - `pause_reason_schema_mismatch_serde_round_trip` — serialize + deserialize SchemaMismatch
   - `pause_reason_tool_error_serde_round_trip` — serialize + deserialize ToolError
   - `pause_reason_unresolved_dependency_serde_round_trip` — serialize + deserialize UnresolvedDependency
   - `pause_reason_json_shape_uses_camel_case` — verify JSON output matches exact camelCase field names (e.g., `"blockedStep"`, `"missingParam"`, `"suggestedTool"`, `"failedStep"`, `"producingStep"`, `"missingFields"`, `"missingOutput"`)
   - `pause_reason_key_constant` — verify WORKFLOW_PAUSE_REASON_KEY value

4. **Property test** in the existing `proptest_workflow` module:
   - `arb_pause_reason()` strategy generating random PauseReason variants
   - `pause_reason_serde_round_trip` proptest verifying any PauseReason survives serialize/deserialize

Follow existing code conventions: snake_case serde for StepStatus enum values, but PauseReason uses camelCase field names (per CONTEXT.md decision). The `tag = "type"` produces `{"type": "unresolvableParams", ...}` style JSON.
  </action>
  <verify>
Run: `cargo test -p pmcp-tasks --lib types::workflow` — all existing tests pass, all new PauseReason tests pass.
Run: `cargo clippy -p pmcp-tasks -- -D warnings` — zero warnings.
  </verify>
  <done>
PauseReason enum exists with 4 variants, serializes to camelCase JSON with tag="type", WORKFLOW_PAUSE_REASON_KEY constant equals "_workflow.pause_reason", all serde round-trip tests pass including property tests.
  </done>
</task>

<task type="auto">
  <name>Task 2: Add retryable field to WorkflowStep and open WorkflowPromptHandler internals to pub(crate)</name>
  <files>
    src/server/workflow/workflow_step.rs
    src/server/workflow/prompt_handler.rs
  </files>
  <action>
**Part A: WorkflowStep retryable field** (`src/server/workflow/workflow_step.rs`)

1. Add `retryable: bool` field to `WorkflowStep` struct, defaulting to `false`. This indicates whether the step's tool is expected to be retryable on transient failure (per CONTEXT.md: "specific tools can declare themselves as retryable"). Placed on WorkflowStep (not tool definition) per research recommendation — workflow author knows which steps are transient.

2. Add builder method: `pub fn retryable(mut self, retryable: bool) -> Self { self.retryable = retryable; self }` — chainable, follows existing builder pattern.

3. Add accessor method: `pub fn is_retryable(&self) -> bool { self.retryable }` — follows the `is_resource_only()` naming pattern.

4. Update all `WorkflowStep` construction sites (the `new()` and `fetch_resources()` constructors) to initialize `retryable: false`.

**Part B: Open WorkflowPromptHandler internals** (`src/server/workflow/prompt_handler.rs`)

Change visibility from private to `pub(crate)` for the following. These are NON-BEHAVIORAL changes — only access control changes, no method body or control flow changes:

1. **`ExecutionContext` struct** — change `struct ExecutionContext` to `pub(crate) struct ExecutionContext`. Change its methods (`new`, `store_binding`, `get_binding`) to `pub(crate) fn`.

2. **WorkflowPromptHandler helper methods** — change all these from `fn` / `async fn` to `pub(crate) fn` / `pub(crate) async fn`:
   - `substitute_arguments` (static method)
   - `fetch_step_resources` (async)
   - `create_user_intent` (sync)
   - `create_assistant_plan` (sync)
   - `create_tool_call_announcement` (sync)
   - `params_satisfy_tool_schema` (sync)
   - `execute_tool_step` (async)
   - `resolve_tool_parameters` (sync)
   - `template_bindings_use_step_outputs` (static method)

**CRITICAL: Do NOT change any method bodies, signatures, or control flow.** Only add `pub(crate)` before `fn`/`async fn`. The composition constraint from Phase 4 is NON-NEGOTIABLE: WorkflowPromptHandler must have zero behavioral modifications.

Run existing workflow tests to verify nothing changed.
  </action>
  <verify>
Run: `cargo test --lib server::workflow` — all existing workflow tests pass unchanged.
Run: `cargo clippy -- -D warnings` — zero warnings in the entire workspace.
Run: `cargo build` — full workspace compiles.
  </verify>
  <done>
WorkflowStep has `retryable` field (default false) with builder method and accessor. ExecutionContext is pub(crate). All 9+ WorkflowPromptHandler helper methods are pub(crate). Zero behavioral changes to WorkflowPromptHandler — all existing tests pass without modification.
  </done>
</task>

</tasks>

<verification>
1. `cargo test -p pmcp-tasks` — all pmcp-tasks tests pass (existing + new PauseReason tests)
2. `cargo test --lib server::workflow` — all workflow tests pass unchanged (proves zero behavioral change)
3. `cargo clippy --workspace -- -D warnings` — zero clippy warnings
4. `cargo build --workspace` — full workspace compiles
5. Verify PauseReason JSON shape manually: `{"type": "toolError", "failedStep": "deploy", "error": "timeout", "retryable": true, "suggestedTool": "deploy_service"}`
</verification>

<success_criteria>
- PauseReason enum with 4 variants serializes correctly to camelCase JSON
- WORKFLOW_PAUSE_REASON_KEY = "_workflow.pause_reason"
- WorkflowStep.retryable field exists, defaults false, has builder + accessor
- ExecutionContext and all WorkflowPromptHandler helpers are pub(crate)
- All existing tests pass without modification (zero behavioral changes)
- Zero clippy warnings
</success_criteria>

<output>
After completion, create `.planning/phases/05-partial-execution-engine/05-01-SUMMARY.md`
</output>
