---
phase: 07-integration-and-end-to-end-validation
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/server/workflow/task_prompt_handler.rs
  - crates/pmcp-tasks/tests/workflow_integration.rs
autonomous: true
requirements:
  - INTG-01
  - INTG-02
  - INTG-04

must_haves:
  truths:
    - "Task-enabled workflow prompt creates a task and returns _meta with task_id in the GetPromptResult"
    - "Non-task workflow registered on the same server returns standard GetPromptResult with _meta: None"
    - "Full lifecycle (create-execute-handoff-continue-complete) works through ServerCore::handle_request"
    - "Step failure produces a handoff with error details and retry guidance"
    - "Cancel-with-result transitions a workflow task to Completed"
  artifacts:
    - path: "src/server/workflow/task_prompt_handler.rs"
      provides: "Fixed task_id extraction from CreateTaskResult JSON"
      contains: "get(\"task\").and_then"
    - path: "crates/pmcp-tasks/tests/workflow_integration.rs"
      provides: "Integration tests for builder API, backward compatibility, and full lifecycle"
      min_lines: 150
  key_links:
    - from: "crates/pmcp-tasks/tests/workflow_integration.rs"
      to: "src/server/workflow/task_prompt_handler.rs"
      via: "ServerCore::handle_request dispatching GetPrompt to TaskWorkflowPromptHandler"
      pattern: "handle_request.*GetPrompt"
    - from: "crates/pmcp-tasks/tests/workflow_integration.rs"
      to: "crates/pmcp-tasks/src/router.rs"
      via: "TaskRouterImpl wired through ServerCoreBuilder::with_task_store"
      pattern: "with_task_store"
---

<objective>
Fix the task_id extraction bug in TaskWorkflowPromptHandler and validate the full task-prompt bridge wiring with integration tests.

Purpose: The task_id extraction bug (value.get("id") returns None because CreateTaskResult serializes as {"task": {"taskId": "..."}}) prevents the entire task-prompt bridge lifecycle from functioning. Fixing it and adding comprehensive integration tests ensures INTG-01 (builder API works), INTG-02 (backward compatibility), and INTG-04 (end-to-end lifecycle) are validated.

Output: Bug fix in task_prompt_handler.rs + new workflow_integration.rs test file covering happy path, error cases, backward compatibility, and cancel-with-result.
</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/07-integration-and-end-to-end-validation/07-CONTEXT.md
@.planning/phases/07-integration-and-end-to-end-validation/07-RESEARCH.md
@src/server/workflow/task_prompt_handler.rs
@crates/pmcp-tasks/tests/lifecycle_integration.rs
@crates/pmcp-tasks/src/router.rs
@src/server/builder.rs
</context>

<tasks>

<task type="auto">
  <name>Task 1: Fix task_id extraction bug in TaskWorkflowPromptHandler</name>
  <files>src/server/workflow/task_prompt_handler.rs</files>
  <action>
Fix the task_id extraction on line 633 of task_prompt_handler.rs. The current code:

```rust
Ok(value) => value.get("id").and_then(|v| v.as_str()).map(String::from),
```

Must be changed to:

```rust
Ok(value) => value
    .get("task")
    .and_then(|t| t.get("taskId"))
    .and_then(|v| v.as_str())
    .map(String::from),
```

Reason: `create_workflow_task` returns a `CreateTaskResult` which serializes as `{"task": {"taskId": "...", ...}, "_meta": null}`. The old extraction path `value.get("id")` always returns `None`, causing every task-aware workflow to fall through to the graceful degradation path (inner handler without task tracking). This is a bug surfaced during Phase 7 research (see 07-RESEARCH.md Pitfall 1).

After the fix, verify the change compiles: `cargo check --lib`
  </action>
  <verify>
Run `cargo check --lib` -- must compile with zero errors. Grep the file to confirm the old `value.get("id")` pattern is gone and replaced with the `value.get("task").and_then(|t| t.get("taskId"))` pattern.
  </verify>
  <done>
The task_id extraction correctly navigates the CreateTaskResult JSON structure. Task-aware workflows will now successfully extract the task_id and proceed with task tracking instead of falling through to graceful degradation.
  </done>
</task>

<task type="auto">
  <name>Task 2: Create workflow integration tests</name>
  <files>crates/pmcp-tasks/tests/workflow_integration.rs</files>
  <action>
Create `crates/pmcp-tasks/tests/workflow_integration.rs` with integration tests that exercise the full task-prompt bridge wiring. Follow the established patterns from `lifecycle_integration.rs` (same directory).

**Test infrastructure (shared helpers):**

1. `build_test_server()` function returning `(ServerCore, Arc<InMemoryTaskStore>)`:
   - Create `InMemoryTaskStore::new().with_security(TaskSecurityConfig::default().with_allow_anonymous(true))`
   - Create `TaskRouterImpl::new(store.clone())`
   - Build `ServerCoreBuilder::new()` with:
     - `.name("test-workflow-integration")`, `.version("1.0.0")`
     - `.tool("fetch_data", FetchDataTool)`, `.tool("transform_data", TransformDataTool)`, `.tool("store_data", StoreDataTool)`
     - `.with_task_store(router)`
     - `.prompt_workflow(task_workflow)` -- a 3-step data pipeline with `.with_task_support(true)`:
       - Step "fetch": tool "fetch_data", arg "source" from prompt arg "source", bind "raw_data"
       - Step "transform": tool "transform_data", arg "input" from step output "raw_data" (data dependency), bind "transformed"
       - Step "store": tool "store_data", arg "data" from step output "transformed"
     - `.prompt_workflow(non_task_workflow)` -- a simple 1-step workflow WITHOUT task support
     - `.stateless_mode(true)` -- skip initialization handshake
   - Return `(server, store)`

2. Tool handler stubs:
   - `FetchDataTool` -- returns `{"data": "raw_content", "source": args["source"]}`. For the error test variant, returns `Err(...)`.
   - `TransformDataTool` -- returns `{"transformed": true, "input": args["input"]}`
   - `StoreDataTool` -- returns `{"stored": true, "location": "db://output"}`

3. `unwrap_result(response: JSONRPCResponse) -> Value` helper (matches lifecycle_integration.rs pattern).

**Test scenarios (all async with `#[tokio::test]`):**

4. `test_backward_compatibility_non_task_workflow` (INTG-02):
   - Build server with both task and non-task workflows
   - Send `GetPrompt` request for the non-task workflow
   - Assert response is a valid `GetPromptResult` with `_meta: None` (or no `_meta` key)
   - This proves non-task workflows are unaffected by task support being enabled on the server

5. `test_task_workflow_creates_task_with_meta` (INTG-01):
   - Build server, send `GetPrompt` for the task-enabled workflow ("data_pipeline")
   - Extract the response, verify `_meta` is present and contains `"taskId"`
   - Verify `_meta` contains step progress information
   - This proves the builder API correctly wires task-aware workflows

6. `test_full_lifecycle_happy_path` (INTG-04):
   - Make `FetchDataTool` return an error to trigger `PauseReason::ToolError`
   - Send `GetPrompt` -- expect handoff with task_id, failed fetch step, unresolved dependency on transform
   - Extract task_id from `_meta`
   - Send `CallTool` for "fetch_data" with `_task_id` in `_meta` (client continuation) -- tool succeeds this time (use a different FetchDataTool variant or accept that the continuation recording is fire-and-forget)
   - Poll `tasks/result` with the task_id -- verify task state reflects progress
   - Send `tasks/cancel` with result to complete the workflow
   - Poll `tasks/result` again -- verify task is `Completed` with the provided result

7. `test_cancel_with_result` (INTG-04 error case):
   - Create a workflow task via `GetPrompt`
   - Send `tasks/cancel` with a result payload
   - Verify task transitions to Completed (not Cancelled)
   - Verify the result is stored

Use the request construction patterns from 07-RESEARCH.md (GetPrompt, CallTool with _task_id, tasks/cancel). Use `RequestId::from(Ni64)` with incrementing IDs. All tests use `handle_request` directly (handler-level, no transport).
  </action>
  <verify>
Run `cargo test --package pmcp-tasks --test workflow_integration -- --test-threads=1` -- all tests must pass. Also run `cargo test --package pmcp-tasks --test lifecycle_integration -- --test-threads=1` to verify existing tests still pass (no regressions). Run `cargo clippy --package pmcp-tasks --tests -- -D warnings` for zero warnings.
  </verify>
  <done>
Integration tests validate: (1) builder API correctly wires task-aware workflows via with_task_store + prompt_workflow (INTG-01), (2) non-task workflows return standard GetPromptResult without _meta on the same server (INTG-02), (3) full create-execute-handoff-continue-complete lifecycle through ServerCore::handle_request (INTG-04), (4) cancel-with-result transitions to Completed (INTG-04 error case).
  </done>
</task>

</tasks>

<verification>
1. `cargo check --lib` passes (bug fix compiles)
2. `cargo test --package pmcp-tasks --test workflow_integration -- --test-threads=1` passes (new tests)
3. `cargo test --package pmcp-tasks --test lifecycle_integration -- --test-threads=1` passes (no regressions)
4. `cargo clippy --package pmcp-tasks --tests -- -D warnings` passes (zero warnings)
5. `cargo clippy --lib -- -D warnings` passes (bug fix has no warnings)
</verification>

<success_criteria>
- Task_id extraction bug fixed: task-aware workflows now extract task_id from CreateTaskResult JSON correctly
- Backward compatibility proven: non-task workflow on same server returns standard response
- Builder API validated: with_task_store + prompt_workflow wiring works end-to-end
- Full lifecycle tested: create -> execute -> handoff -> continue -> complete through real ServerCore
- All existing tests continue to pass (zero regressions)
</success_criteria>

<output>
After completion, create `.planning/phases/07-integration-and-end-to-end-validation/07-01-SUMMARY.md`
</output>
