> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pflow.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Product updates and announcements

<Update label="May 2026" description="v0.13.0" tags={["New releases", "Improvements", "Breaking changes"]}>
  ## Declarative prompt caching and `pflow analyze-cache`

  We introduced provider-level prompt caching as a declarative workflow surface. By defining a top-level `## Cache` section and declaring chunks on your LLM nodes, prompt prefixes are cached directly at the provider level (such as Anthropic and Gemini), dramatically reducing costs and latency for repetitive system instructions and context.

  ````markdown theme={null}
  ## Cache
  - ttl: 5m

  ```cache [brief]
  The project brief we are working from:
  ${brief}
  ```

  ### extract-features
  - type: llm
  - prompt_cache: [brief]
  - prompt: Extract features from ${input.text}
  ````

  To help discover caching opportunities, the new `pflow analyze-cache` command performs static and trace-driven analysis of your workflow. It estimates cacheable tokens, models per-call cost projections, and provides actionable, headline-led recommendations without executing live model calls.

  ```bash theme={null}
  # Analyze cache efficiency and get optimizations from a previous execution trace
  pflow analyze-cache my-workflow.pflow.md --from-trace
  ```

  <Tip>
    Provider minimums apply for prompt caching (e.g., 1024 tokens for Anthropic Claude 4.5 Sonnet, 4096 tokens for Gemini pro models and 2048 for flash models). `pflow analyze-cache` automatically matches your prompt sizes against the target model's specific thresholds to prevent you from writing cache blocks that are too small to activate.
  </Tip>

  **Highlights**

  * Added the top-level `## Cache` markdown section to parse declarative, reusable prompt chunks.
  * Added the `prompt_cache` and `prewarm` properties to LLM nodes to control cache inclusion and gate automatic batch-prefix caching.
  * Implemented the `pflow analyze-cache` CLI command (and matching MCP tool) with Greenfield, Steady-state, Trace-driven, and Already-optimal analysis modes.
  * Added a 22-entry warning catalog to flag cache misalignments, prompt shadow duplicates, and missing shared references.
  * Implemented dynamic cache TTL translation for Gemini models (`cachedContents`) alongside Anthropic native controls.
  * Added support for multi-breakpoint prompt caching for Anthropic models.
  * Implemented synthetic cache warmup capabilities and detailed cost telemetry in trace formats.
  * Fixed prewarm diagnostics to correctly account for declared `prompt_cache` chunks.

  ## Native structured outputs for Claude Code

  We replaced Claude Code's prompt-injected and regex-extracted structured output system with native JSON Schema support using `claude_agent_sdk`'s native structured output capabilities. This ensures guaranteed schema compliance without competing system instructions or fragile string parsing.

  ````markdown theme={null}
  ### audit-code
  - type: claude-code
  - max_turns: 5
  - prompt: Audit the security of the root directory.

  ```yaml output_schema
  type: object
  properties:
    vulnerabilities:
      type: array
      items:
        type: string
  required: [vulnerabilities]
  ```
  ````

  <Note>
    Claude Code structured output failures are now treated as "soft failures." If the session fails to comply with the schema, the raw text is preserved, a `_schema_error` is set, and the run continues in a `DEGRADED` status rather than raising a hard error that aborts your workflow.
  </Note>

  **Highlights**

  * Upgraded the Claude Code node to use `ResultMessage.structured_output` via the native SDK, requiring a minimum of `claude-agent-sdk>=0.2.82`.
  * Added preflight and static validation to enforce that Claude Code output schemas define a top-level `type: object`.
  * Added a strict constraint requiring `max_turns >= 2` when an output schema is defined to allow planning and output turns.
  * Implemented warning rehydration inside the persistent memoization cache, ensuring that cached soft-failures correctly replay as `DEGRADED` rather than passing as successful.
  * Narrowed exception handling within Claude Code sessions to `ProcessError` to allow actionable SDK remediation messages (like installation and doctor checks) to bubble up.
  * Aligned the Claude node's output parsing by linking the "Outputs" parse-hint with the accepted `source` key, and stripped runtime-internal vocabulary from agent error messages.

  ## LiteLLM integration and offline pricing

  We rebuilt our LLM execution layer, replacing the existing integration with a pflow-owned adapter backed natively by LiteLLM. This migration unifies over 100 model providers under a single, robust seam with unified exception mapping and cost tracking.

  ```bash theme={null}
  # Discover environment variables required for your configured LLM models
  pflow settings llm providers
  ```

  **Highlights**

  * Replaced the `llm` package with a lazy-imported LiteLLM wrapper, improving overall CLI startup performance.
  * Added a typed exception hierarchy under `LLMCallError` to provide structured discriminators (`UnknownModelError`, `MissingApiKeyError`, `LLMTransientError`) for downstream retry and fallback routing.
  * Redesigned tracing to use a `shared["__trace_collector__"]` save/restore context, fixing a thread-boundary bug where literal prompt captures were silently omitted.
  * Added a deterministic offline pricing map for LiteLLM models to ensure accurate cost estimation when pricing metadata is missing from local snapshots.
  * Added automatic bare model name prefixing (e.g., `gpt-4o` normalized to `openai/gpt-4o`) to ensure correct routing.
  * Fixed a pricing bug where `cost_usd` was left unpopulated for newer LLM models released after our bundled LiteLLM snapshot.
  * Upgraded the default LiteLLM engine version and fixed `reasoning_effort` mapping for Anthropic Claude 3 Opus.

  ## CLI usability and parser refinement

  We refined our CLI output, added more helpful diagnostics to failing pipelines, and improved the core Markdown parser to handle complex YAML block structures.

  **Highlights**

  * Added support for dotted paths inside CLI output destination flags (e.g. `-o data.nested.field`).
  * Added walk-to-failure hints to the terminal output on workflow failures to assist agents in debugging sequence halts.
  * Implemented a more compact summary representation for large batch node executions.
  * Fixed linting for shell nodes without template inputs to offer both available cache resolutions in the warning text.
  * Fixed the markdown parser to preserve blank lines inside multi-line YAML block scalars, preventing layout corruption of templates and prompts.

  <Accordion title="Breaking changes">
    ### Claude Code Python-alias schemas removed

    Legacy Python-alias type names (`str`, `int`, `list`, `dict`) are no longer supported inside Claude Code `output_schema` blocks. All schemas must use standard JSON Schema types and declare a top-level `type: object`.

    <CodeGroup>
      ```yaml Legacy Schema (Deprecated) theme={null}
      risk_level: str
      issues: list
      ```

      ```yaml JSON Schema (Required) theme={null}
      type: object
      properties:
        risk_level:
          type: string
        issues:
          type: array
          items:
            type: string
      required: [risk_level, issues]
      ```
    </CodeGroup>

    ### Claude Code Turn minimums

    Workflows declaring a Claude Code node with an `output_schema` must set `max_turns` to `2` or greater. Workflows specifying `max_turns: 1` will fail validation.

    ### AdapterResponse shape changes

    The internal response object from LLM calls no longer carries `error` or `status` fields. Failure states now consistently raise a subclass of `LLMCallError`. Downstream steps catching custom exceptions should catch `LLMCallError` and read structured attributes (`kind`, `reason`).

    ### LiteLLM package pinning and model names

    * Simon Willison's `llm` CLI and its associated provider plugins are no longer used. Environment variables (like `OPENAI_API_KEY`) are read directly from the shell or via `pflow settings`.
    * LLM nodes require provider-prefixed model names (e.g. `openai/gpt-4o` instead of `gpt-4o`). Bare names are auto-prefixed based on common naming conventions, but unknown bare names will pass through unchanged.

    ### Caching flag behavior

    The `--no-cache` CLI flag now only bypasses local pflow memoization reads. It does not disable LLM provider-level prompt caching.
  </Accordion>
</Update>

<Update label="April 2026" description="v0.12.0" tags={["New releases", "Improvements", "Breaking changes"]}>
  ## Execution previews and planning

  We introduced a high-fidelity execution planner via the `--dry-run` flag. It provides historical LLM cost and duration estimates without invoking side effects, using the same cache-key and template-resolution logic as the live engine to ensure zero drift.

  ```bash theme={null}
  # Preview execution with cost and duration estimates
  pflow workflow.pflow.md --dry-run
  ```

  <Note>
    The planner now supports full per-item recursion for batch sub-workflows, allowing you to see exactly which items in a large parallel run would execute and which would serve from cache.
  </Note>

  **Highlights**

  * Added the `--dry-run` flag to provide execution plans with historical cost (\$USD) and duration estimates.
  * Implemented recursive batch planning that aggregates child summaries into a single synthetic plan, correctly handling parallel vs. sequential durations.
  * Added a `cost_basis` indicator (`upper_bound` vs `exact`) to dry-run summaries to help agents gate high-cost operations.
  * Fixed dry-run recursion for batch sub-workflows to prevent false validation errors on `${item}`-backed child inputs.

  ## Modernized CLI and guidance

  The CLI has been flattened into a focused, top-level surface, and the monolithic agent instructions have been replaced by `pflow guide`—a topic-scoped system that delivers framework and node-specific guidance at runtime.

  ```bash theme={null}
  # Access topic-scoped guidance for agents or users
  pflow guide llm code batch
  ```

  **Highlights**

  * Flattened the CLI surface: `pflow workflow <verb>` and `pflow registry <verb>` are now top-level commands like `pflow list`, `pflow describe`, and `pflow probe`.
  * Added `pflow guide`, a content delivery system that composes help topics based on requested scope or auto-detected workflow features.
  * Improved non-interactive output routing: live progress now streams to `stderr` in real-time, while node data is routed strictly to `stdout` for clean piping to `jq`.
  * Added an exception boundary to the MCP server via a `FastMCP` subclass to provide structured diagnostics to agents.
  * Consolidated `pflow trace report` into the flattened `pflow report` command.
  * Added confidence-based guidance and runnable command hints to the `pflow find` output.

  ## Type safety and native diagnostics

  We refactored the workflow type vocabulary to use canonical JSON Schema names and moved the validation engine to produce structured `Diagnostic` objects natively. This ensures that typos and contract violations are caught early with rich, actionable "Did you mean?" suggestions.

  <Tip>
    The new type vocabulary is strictly enforced at the sub-workflow boundary. Any value crossing from parent to child must be declared on the child workflow, or it will be rejected at parse time.
  </Tip>

  **Highlights**

  * Refactored the type vocabulary to 7 canonical names: `string`, `integer`, `number`, `boolean`, `array`, `object`, and `any`.
  * Upgraded the validator to produce `Diagnostic` objects natively, enabling numbered lists, fuzzy suggestions, and source-line tracking in all validation errors.
  * Implemented a strict parent-to-child input boundary for sub-workflows that rejects undeclared inputs at both parse-time and runtime.
  * Added validate-time type checking for Python code-node input and result/next annotations.
  * Unified the `??` coalesce operator to correctly distinguish between nodes that were skipped and nodes that failed.
  * Fixed workflow and template validators to short-circuit on structural errors, preventing cascades of redundant messages.

  ## Rich Mermaid visualization

  The Mermaid visualization system has been transformed into a full data-flow engine. It now renders sub-workflow boundaries, external IO subgraphs, and data-provenance edges derived from template references.

  ```bash theme={null}
  # Visualize data-flow and batch semantics in Mermaid format
  pflow visualize my-workflow.pflow.md --descriptions
  ```

  **Highlights**

  * Added support for "External IO" wrappers that render sub-workflow inputs and outputs as dashed subgraphs outside the primary pipeline.
  * Added data-flow edges that trace the actual provenance of data through `${node.field}` template references.
  * Implemented the Mermaid `procs` shape (stacked rectangles) to visually communicate batch parallelism.
  * Added `--descriptions` support to the visualizer to include node-level documentation in the rendered output.
  * Improved layout logic to connect top-level inputs directly to their nearest consumer, preventing long-range edges from distorting the diagram.

  ## Reliability and invariants

  This release addresses several critical execution invariants, particularly how the shared store handles node failures and how batch results are filtered.

  **Highlights**

  * Fixed the failed-node invariant: data from failed nodes now moves to `shared["__failures__"]` instead of leaking into `shared[node_id]`, preventing downstream nodes from accidentally reading partial or failed results.
  * Updated batch processing to exclude failed items from the `results` array, ensuring downstream nodes receive only successful data.
  * Added protection against `__dunder__` parameter names to prevent workflows from accidentally overwriting internal framework state.
  * Implemented a `_ProgressPartialLineFilter` to prevent `logger.warning` messages from corrupting live progress lines.
  * Added automatic node registry refreshing when source files change on disk.
  * Fixed on-error recovery reporting to correctly reflect a `DEGRADED` status instead of a false `SUCCESS`.

  <Accordion title="Breaking changes">
    ### Flattened CLI commands

    The hierarchical `workflow` and `registry` namespaces have been removed.

    * `pflow workflow save` → `pflow save`
    * `pflow workflow history` → `pflow history`
    * `pflow registry run` → `pflow probe`
    * `pflow instructions` → `pflow guide`
    * `pflow mcp tools` → `pflow mcp list`

    ### Type vocabulary refactor

    Python type aliases (`str`, `int`, `list`, `dict`) are no longer supported in `## Inputs` or `## Outputs`.

    * Use `string` instead of `str`.
    * Use `integer` instead of `int`.
    * Use `array` instead of `list`.
    * Use `object` instead of `dict`.
    * Use `any` for wildcards.

    ### Sub-workflow input strictness

    * The `workflow_ir` inline-IR escape hatch has been removed. Use file references instead.
    * Sub-workflows now reject undeclared inputs. Any parameter passed from a parent must be explicitly declared in the child's `## Inputs` section.

    ### Failed-node data location

    Data from failed nodes is no longer available at `shared[node_id]`. It is archived in `shared["__failures__"][node_id]`. Standard template references to failed nodes will now correctly trigger "node did not execute" errors unless the coalesce operator (`??`) is used.

    ### Batch result filtering

    When `error_handling: continue` is used, the `results` list in the batch output no longer contains `None` or error objects for failed items; it only contains successful results.
  </Accordion>
</Update>

<Update label="April 2026" description="v0.11.0" tags={["New releases", "Improvements", "Breaking changes"]}>
  ## Execution core and iteration speed

  We rebuilt the execution core with a standalone orchestration engine that compiles workflows once per batch, dropping compilation overhead to near zero. A new SQLite-backed memoization cache lets you iterate rapidly by re-using results for unchanged nodes.

  ```bash theme={null}
  # Re-run a workflow, executing only 'target-node' and its dependencies
  pflow my-workflow.pflow.md --only target-node
  ```

  <Note>
    The memoization cache is automatically propagated to all nesting levels, meaning unchanged sub-workflows and nodes safely serve cached results without re-executing.
  </Note>

  **Highlights**

  * Redesigned the execution core to compile sub-workflows once per batch, yielding a \~7x speedup for large parallel iterations.
  * Added a persistent memoization cache system with `--only` and `--no-cache` CLI flags for precise, rapid iteration control.
  * Added per-node cache opt-out support via the `cache: false` property.
  * Fixed concurrent mutation issues in the workflow executor during compilation.
  * Fixed an intermittent bug where zombie threads caused stream corruption (`I/O operation on closed file`) in Python code nodes.

  ## Execution reports and traces

  The trace system has been redesigned from flat JSON snapshots to a tree-structured format. You can now generate navigable Markdown reports that display exact rendered prompts, outputs, and LLM costs for every node.

  ```bash theme={null}
  # Generate a directory of Markdown execution reports
  pflow my-workflow.pflow.md --report-dir ./report/
  ```

  <Tip>
    Because execution reports are saved as standard directories of Markdown files, you can use `git diff report/` to easily compare prompt renders and outputs between workflow runs.
  </Tip>

  **Highlights**

  * Added the `--report` flag to generate a directory of Markdown files detailing execution, including input/output token breakdowns and rendered templates.
  * Upgraded trace format to 2.0.0: uses tree-structured events that correctly nest batch items and sub-workflows without truncating data.
  * Added smart anomaly detection in reports to flag empty outputs (like dropped HTTP bodies) and provide template fix suggestions.
  * Cross-cutting infrastructure keys and LLM costs now propagate correctly through deeply nested sub-workflows.
  * Added LLM parameters (temperature, reasoning effort, system prompt) to node metadata in reports.
  * Fixed cache invalidation for sub-workflow changes and eliminated phantom cost reporting in traces.

  ## File references and workflow bundling

  Code block parameters can now reference external files directly. To support this safely, `pflow workflow save` now bundles workflows and their file dependencies into self-contained directories.

  ```markdown theme={null}
  ### analyze-code
  - type: llm
  - prompt: ./prompts/code-review.md
  ```

  **Highlights**

  * Code-block parameters (`prompt`, `code`, `command`, `batch`, `output_schema`) can now reference external files. The system auto-detects paths, reads the files, and resolves templates inside them at compile time.
  * `pflow workflow save` now packages workflows as folders containing the entry point and all referenced dependencies, preserving relative directory structures.
  * Added per-item parameter overrides in batch nodes — each item can set its own `model`, `reasoning_effort`, or any other node parameter, so a single batch can mix providers or effort levels.
  * Added the `inputs` parameter as template context for all node types, not just code nodes.
  * Fixed relative path resolution so sub-workflow dependencies always resolve against the sub-workflow's directory, not the current working directory.
  * File references in sub-workflows are now fully resolved before validation.

  ## Validation and diagnostics

  We rebuilt the CLI error output pipeline and validation engine. Every error now renders in a single diagnostic format — title, location, context block, and fix suggestion (e.g., "Did you mean 'file\_path'?") — consistent across text and JSON output. The parser catches deeper structural issues before execution begins.

  ```bash theme={null}
  # Visualize workflow structure using Mermaid
  pflow visualize workflow.pflow.md --direction TD
  ```

  **Highlights**

  * Added the `pflow visualize` command to generate Mermaid flowcharts of workflow topologies.
  * Unified diagnostic rendering into a single, structured format with self-describing exceptions for both CLI text and JSON outputs.
  * Added recursive sub-workflow validation at parse time to catch structural errors, unknown node types, and missing required inputs before execution.
  * Added a 120-second default timeout to LLM nodes to prevent workflows from hanging indefinitely on stalled API calls.
  * LLM nodes now catch `JSONDecodeError` when `output_schema` fails, preserving the raw text in the response and returning a soft error.
  * Improved the markdown parser to detect orphaned content, duplicate section headings, and provide actionable errors for unquoted colons in YAML parameters.
  * Fixed a bug where type annotations in Python code YAML inputs (e.g., `text: str = ${ref}`) were incorrectly parsed as literal values.
  * Unified output auto-detection across CLI, JSON, and MCP interfaces.
  * Added a CLI hint suggesting `pflow mcp sync` when a registry search returns no results but matching MCP servers exist.
  * Fixed an issue where the registry cache would not refresh after a package upgrade.
  * Fixed issues with template deduplication, double validation, raw string parsing in single-line YAML, and bash syntax correctness.
  * Fixed missing error details in JSON output, dead code in error display paths, and redundant template resolution.

  ## Under the hood

  This release includes a major structural overhaul: 18,000 lines of production code removed across 16 refactoring PRs. The PocketFlow framework was replaced with a standalone orchestration engine, the exception hierarchy was consolidated under a single base class, and the CLI, compiler, and runtime were each decomposed into focused modules. These changes don't add features directly — they're what made the compile-once optimization, unified diagnostics, and shared execution pipeline possible.

  <Accordion title="Breaking changes">
    ### Workflow save format

    Workflows are now saved as folders (`~/.pflow/workflows/{name}/{name}.pflow.md`), not flat files. This change enables proper file dependency bundling but will break existing symlinks for published skills.

    ### Unknown parameters block execution

    Unknown parameter detection was promoted from non-blocking warnings to blocking validation errors to catch typos early. Typos like `- path:` instead of `- file_path:` will now prevent execution.

    ### Unified JSON error shape

    All error paths in the CLI now produce a single, unified JSON shape.

    <CodeGroup>
      ```json Before theme={null}
      {
        "success": false,
        "is_error": true,
        "failed_node": "fetch"
      }
      ```

      ```json After theme={null}
      {
        "success": false,
        "status": "failed",
        "error": "Execution failed",
        "errors": [{"message": "...", "category": "..."}]
      }
      ```
    </CodeGroup>

    ### Batch error handling

    * Batch nodes now abort with a `RuntimeError` when all items fail and `error_handling: continue` is set, rather than returning garbage data.
    * Batch nodes no longer swallow compilation errors under `continue` mode.
    * When `continue` mode recovers from partial failures, it now returns a `"default"` action and sets a `DEGRADED` status instead of returning an `"error"` action that halts the workflow.
    * Sub-workflow error actions and permissive batch template errors are now properly propagated and detected in batch processing.

    ### Removal of internal nodes

    The experimental `git`, `github`, `test`, and `echo` nodes have been completely removed from the registry and documentation.

    ### Workflow termination

    The engine now recognizes `"end"` and error-only successors as intentional workflow terminations rather than validation failures.

    ### Trace format and LLM calls

    The parallel `__llm_calls__` accumulator was removed. Trace events are now the single source of truth for LLM costs. Trace format has been bumped to 2.0.0, removing all value truncation and replacing full-store snapshots (`shared_before`/`shared_after`) with focused per-node parameters.
  </Accordion>
</Update>

<Update label="March 2026" description="v0.10.0" tags={["New releases", "Improvements", "Bug fixes", "Breaking changes"]}>
  ## Branch convergence

  Workflows now support branch convergence. When downstream nodes need to reference "whichever branch ran" after a conditional split, you can use the new coalesce operator (`??`) or optional inputs in code nodes to handle skipped branches.

  ```markdown theme={null}
  ### summarize
  - type: llm
  - prompt: Result was: ${branch-high.stdout ?? branch-low.stdout}
  ```

  <Tip>
    The coalesce operator checks if the root node actually executed. If `branch-high` was skipped, it falls through to `branch-low`. If the node *did* execute but you misspelled the field (e.g., `stddout`), it catches the typo and raises an error instead of silently falling through.
  </Tip>

  **Highlights**

  * Added the `??` coalesce operator for template syntax (`${a.stdout ?? b.stdout}`). The resolver tries each operand left-to-right and skips branches that did not execute.
  * Coalesce is supported in all template contexts: inline strings, shell commands, LLM prompts, input dicts, workflow output sources, and batch items.
  * Python code nodes now accept `Optional[T]` or `T | None` input annotations. If the source branch didn't execute, `None` is injected automatically instead of raising a runtime error.

  ## Nested workflows

  Workflow nodes now look and behave like every other node type. You pass parameters as regular inputs, and child outputs are exposed via the standard namespace system.

  ```markdown theme={null}
  ### process-document
  - type: workflow
  - workflow: ./child.pflow.md
  - title: "Hello World"
  - body: ${read-file.content}
  ```

  <Note>
    Child workflow inputs are validated before execution starts. If you miss a required input or provide the wrong parameter name, the parent workflow fails immediately with an error listing the child's declared inputs.
  </Note>

  **Highlights**

  * Unified `workflow` parameter handles both file paths and saved workflow names.
  * Non-reserved parameters become child inputs.
  * If the child workflow declares `## Outputs`, they are exposed to the parent via standard dot notation (`${node_id.output_name}`).
  * Fixed relative path resolution so `./child.pflow.md` always resolves from the parent workflow's directory, even across deep nesting levels.
  * The template validator now statically resolves child workflow outputs to catch typos during compilation.

  ## LLM reasoning and cost tracking

  You can now control reasoning and thinking depth across all LLM providers using a unified interface. pflow translates your settings to the provider-specific parameters — Anthropic's `thinking_budget`, OpenAI's `reasoning_effort`, Gemini's thinking config.

  ```markdown theme={null}
  ### analyze-data
  - type: llm
  - model: claude-sonnet-4-5
  - reasoning_effort: high
  - prompt: Analyze this dataset...
  ```

  **Highlights**

  * Added `reasoning_effort` (xhigh, high, medium, low, minimal, none) and `reasoning_max_tokens` (direct token budget) to the LLM node.
  * Added a `model_options` parameter to the LLM node as an escape hatch for provider-specific fields.
  * Unified LLM cost access: `${node.llm_usage.cost_usd}` now works in workflow templates for both standard LLM and Claude Code nodes.
  * LLM costs are computed at execution time, making `cost_usd` available in the shared store immediately after each node runs.
  * The Claude Code node's redundant `_claude_metadata` output was removed; all metadata is consolidated into `llm_usage`.

  ## Validation and execution robustness

  Several compile-time and runtime edge cases around batch processing, validation depth, and parallel execution have been fixed.

  **Highlights**

  * The template validator now infers the internal structure of batch items based on the upstream batch source, catching invalid `${item.field}` references at compile time with "did you mean?" suggestions.
  * Validation now recurses into nested dictionaries and lists (such as the `inputs` dict on code nodes) to catch typos and non-existent forward references.
  * Required workflow inputs now strictly fail validation when provided as empty strings.
  * Batch nodes now return an "error" action on partial failures when `error_handling: continue` is set, enabling proper `on-error` routing for partial batch failures.
  * Fixed a bug where the validator blocked batch processing entirely on nested workflow nodes.
  * Resolved a `_thread.RLock` pickle error that caused parallel batch processing to crash at runtime.

  **Other**

  * Added `--timeout` and `--sse-timeout` flags to `pflow mcp add` so custom timeout values are preserved in config files.

  <Accordion title="Breaking changes">
    ### Removal of planning module and repair system

    The built-in natural language planning module and auto-repair systems have been removed (\~40,000 lines of code). AI agents handle planning directly — pflow provides the runtime, validation, and execution primitives they compose.

    * Removed CLI flags: `--trace-planner`, `--planner-timeout`, `--planner-model`, `--auto-repair`, `--cache-planner`, `--save/--no-save`, `--no-update`, and `--generate-metadata`.
    * Component and workflow discovery features have been preserved as plain functions for agents to query available resources.

    ### Nested workflow API

    * The `workflow_ref` and `workflow_name` parameters have been consolidated into a single `workflow` parameter.
    * `param_mapping` and `output_mapping` have been removed entirely. Pass arguments directly as inputs, and access outputs via standard dot notation.
    * `isolated` and `scoped` storage modes have been removed.

    <CodeGroup>
      ```yaml Before (v0.9.0) theme={null}
      ### process
      - type: workflow
      - workflow_ref: ./child.pflow.md
      - param_mapping:
          text: ${source.text}
      - output_mapping:
          summary: child_summary
      ```

      ```yaml After (v0.10.0) theme={null}
      ### process
      - type: workflow
      - workflow: ./child.pflow.md
      - text: ${source.text}
      # Outputs are automatically mapped to ${process.summary}
      ```
    </CodeGroup>

    ### Unresolvable output errors

    When a workflow output source references a node that didn't execute (e.g., a branch not taken) and no `??` coalesce operator is used, it now raises an `OutputResolutionError` with a precise diagnostic message. Previously, these unresolvable outputs were silently dropped, causing confusing downstream failures in nested workflows.

    ### Removed registry run timeout flag

    The `--timeout` flag on the `pflow registry run` command has been removed as it was redundant. Timeouts can be passed directly as a per-node parameter (e.g., `timeout=30`).
  </Accordion>
</Update>

<Update label="March 2026" description="v0.9.0" tags={["New releases", "Improvements", "Bug fixes", "Breaking changes"]}>
  ## Conditional branching and loops

  Workflows now support conditional routing. You can branch based on errors, make data-driven routing decisions in Python code nodes, and create retry loops directly in your `.pflow.md` files.

  ````markdown theme={null}
  ### router
  - type: code
  - on-error: error-handler

  ```python code
  data: dict
  if data["category"] == "premium":
      next: str = "premium-handler"
  else:
      next: str = "standard-handler"
  ```
  ````

  <Note>
    To prevent infinite loops, an automatic loop guard tracks node visits and raises a `MaxNodeVisitsError` if a single node is executed 100 times (configurable via `PFLOW_MAX_NODE_VISITS`).
  </Note>

  **Highlights**

  * Added `- next:`, `- on-error:`, and `- next: end` syntax for static and error routing.
  * Python code nodes support a `next` variable for dynamic, data-driven routing.
  * Caching is now automatically invalidated when a node is revisited in a loop, ensuring exit conditions are correctly re-evaluated.
  * `flow.run()` is now always wrapped to ensure visit counts reset correctly between executions.
  * Topological sorting now uses position-based edge filtering to allow valid data dependencies while preventing cycle errors on backward loop edges.

  ## Guaranteed structured JSON

  The LLM node now accepts an `output_schema` parameter for guaranteed structured JSON responses. It uses the constrained decoding APIs of model providers (Anthropic, Gemini, OpenAI) instead of prompting for JSON and hoping the model complies.

  ````markdown theme={null}
  ### extract-entities
  - type: llm
  - prompt: Extract entities from ${read.content}

  ```yaml output_schema
  type: object
  properties:
    people:
      type: array
      items:
        type: string
  required:
    - people
  ```
  ````

  <Tip>
    When `output_schema` is provided, the node parses the response directly into a dictionary. Downstream nodes can access fields immediately via `${extract-entities.response.people}` without an intermediate extraction step.
  </Tip>

  **Highlights**

  * `yaml output_schema` code blocks pass JSON Schema dicts directly to the `llm` library.
  * The API response is parsed and stored as a `dict`, avoiding downstream string parsing.
  * Code block stripping is safely skipped when a schema is set, since the API returns clean JSON.

  ## Stateful MCP servers and error reporting

  MCP servers are no longer restarted for every node in a workflow. A background event loop now acts as a connection pool, keeping server sessions alive across workflow steps. This fixes silent failures where stateful servers (like Playwright browsers or database clients) lost all state between step executions.

  **Highlights**

  * Persistent connection pool keeps both `stdio` and `http` MCP sessions alive across nodes.
  * Automatic crash recovery evicts and retries sessions once if a transport error (like a broken pipe) occurs.
  * MCP error reporting now un-wraps internal `ExceptionGroup` task failures to show the actual HTTP error (e.g., "Authentication failed" instead of a raw 40-line traceback).
  * Fixed a logging bug that prepended "MCP tool failed:" twice.
  * Node output formatter correctly detects `error` keys so MCP failures show as "failed" instead of "succeeded".

  <Accordion title="Breaking changes">
    ### Explicit branch target routing

    Nodes reached via explicit routing (branch targets) used to silently fall through to the next node in document order. This silent, input-dependent bug has been fixed via parse-time validation. Any node targeted by an action edge must now explicitly declare its next step.

    <CodeGroup>
      ```yaml Before (v0.8.0) theme={null}
      ### router
      - type: code
      - on-error: error-handler

      ### error-handler
      - type: shell
      # Silently fell through to the next node below it
      ```

      ```yaml After (v0.9.0) theme={null}
      ### router
      - type: code
      - on-error: error-handler

      ### error-handler
      - type: shell
      - next: end # Explicit routing now required
      ```
    </CodeGroup>

    ### Dynamic routing validation

    If a Python code node assigns a variable to `next` (e.g., `next = target_var` instead of a literal `"node-id"`), you must explicitly declare `- next:` in the markdown step parameters so the parser can build the execution graph.

    ### ReadFile outputs raw content

    The `ReadFile` node previously prepended line numbers (`N: `) unconditionally to every line it read. This corrupted file content for downstream LLM prompts, templates, and configurations. It has been completely removed; the node now returns raw, unmodified file content.

    ### Python code node results

    The `result` output variable is now optional in Python code nodes as long as `next` is declared.
  </Accordion>
</Update>

<Update label="February 2026" description="v0.8.0" tags={["New releases", "Improvements", "Bug fixes"]}>
  First public release on PyPI. pflow is a CLI workflow engine — AI agents
  write `.pflow.md` files that chain shell commands, LLM calls, HTTP requests,
  and Python code through a shared data store. Workflows run the same way
  every time, without burning tokens on repeated tool calls.

  ```bash theme={null}
  uv tool install pflow-cli
  ```

  ## Agent skills

  You can now publish workflows as native skills for AI agents. The `pflow skill`
  command symlinks your saved workflows to the configuration directories for
  Claude Code, Cursor, GitHub Copilot, and Codex.

  ```bash theme={null}
  # Publish a workflow to multiple tools at once
  pflow skill save pr-analyzer --cursor --copilot
  ```

  <Tip>
    Published skills are symlinks, not copies. When you edit the original workflow,
    the agent's skill updates instantly without needing to re-publish.
  </Tip>

  **Highlights**

  * `pflow skill save` enriches workflows with usage sections and metadata for the agent.
  * Support for multiple targets: `--cursor`, `--copilot`, `--codex`, and `--personal`.
  * `pflow workflow history` shows execution stats and last-used inputs.
  * Improved discovery matching by including input names and node IDs in the context.

  ## Data integrity

  LLM nodes no longer discard prose when extracting JSON. Previously, if a response
  contained a JSON block, the node threw away the surrounding text. Now, the full
  response is stored as a string, and JSON parsing happens on-demand via the
  template system.

  **Highlights**

  * LLM nodes preserve prose explanations alongside code blocks.
  * JSON fields are still accessible via dot notation: `${node.response.field}`.
  * Numeric strings (like Discord IDs) declared as `type: string` are no longer coerced to integers.
  * Batch node error messages now correctly list available outputs for inner items.
  * Workflow frontmatter tracks average execution duration for performance monitoring.

  ## Developer experience

  Runtime errors in Code nodes now point to the exact line number in your
  `.pflow.md` file, rather than the temporary Python script. We also improved
  environment variable handling in MCP configurations to support dynamic URLs.

  **Highlights**

  * Code node errors show `Location` and `Source` fields with correct line mapping.
  * MCP server configs now expand environment variables in URLs and `settings.json`.
  * Markdown parser specifically detects and explains nested backtick errors.

  <Accordion title="Package name">
    The PyPI package is `pflow-cli`, not `pflow` (that name was already taken).
    This is the first PyPI release — if you installed from git before, switch to:

    ```bash theme={null}
    uv tool install pflow-cli
    # or
    pipx install pflow-cli
    ```
  </Accordion>
</Update>

<Update label="February 2026" description="v0.7.0" tags={["New releases", "Breaking changes"]}>
  ## Workflows are documentation

  Workflows have moved from JSON to a custom Markdown format (`.pflow.md`). The
  file *is* the documentation — H1 headers become titles, prose becomes descriptions,
  and code blocks define execution logic. Comments and formatting are preserved
  when saving, so your notes survive round-trips through the CLI.

  ```markdown theme={null}
  # Daily Report
  ## Steps
  ### fetch-data
  - type: http
  - url: https://api.github.com/repos/owner/repo/issues

  ### summarize
  - type: llm
  - prompt: Summarize these issues: ${fetch-data.response}
  ```

  <Note>
    The internal parser produces the exact same IR structure as before, so
    execution logic is unchanged. The migration is purely about authoring
    experience and LLM readability.
  </Note>

  **Highlights**

  * New `.pflow.md` extension with YAML frontmatter for metadata.
  * Line-by-line error reporting with context, replacing JSON syntax errors.
  * "Save" operations update the file in place, preserving your comments.
  * `pflow workflow save` extracts the description directly from the document prose.

  ## Native Python execution

  The new `code` node runs Python in-process, passing native objects (lists,
  dicts) between steps without serialization overhead. Unlike the shell node,
  it doesn't need `jq` to parse inputs — `inputs` are injected directly as local
  variables.

  ````markdown theme={null}
  ### transform-data
  - type: code
  - inputs:
      data: ${fetch.response}
      limit: 10

  ```python code
  data: list
  limit: int

  result: list = data[:limit]
  ```
  ````

  **Highlights**

  * Zero-overhead data passing for heavy transformations.
  * Required type annotations catch type mismatches before execution.
  * `stdout`/`stderr` capture for debugging, with configurable timeouts.

  ## Unix piping and validation

  You can now chain workflows using standard Unix pipes. Mark an input with
  `stdin: true`, and pflow will route piped data to that specific parameter.
  Validation has also been unified: the checks that run during `--validate-only`
  now run before every execution, catching errors like invalid JSON string
  templates before any steps run.

  ```bash theme={null}
  # Chain workflows with the -p (pipe) flag
  pflow -p fetch-logs | pflow -p parse-logs | pflow analyze-errors
  ```

  <Tip>
    The validator now detects the "JSON string with template" anti-pattern
    (e.g., `"body": "{\"val\": \"${var}\"}"`) and suggests the correct object
    syntax to prevent runtime JSON errors.
  </Tip>

  **Highlights**

  * `stdin: true` input property for explicit pipe routing.
  * FIFO detection prevents hangs when no input is piped.
  * Unified validation logic ensures `--validate-only` matches runtime behavior.
  * Improved error messages for unknown node types (no more stack traces).
  * `disallowed_tools` parameter on Claude Code nodes to block specific tools in agentic workflows.
  * Fixed nested template validation for `${item.field}` inside array brackets.

  <Accordion title="Breaking changes">
    ### Workflow format

    JSON workflow files (`.json`) are no longer supported. Existing workflows
    must be converted to the `.pflow.md` format. The CLI will reject JSON files
    with a migration error.

    ### Stdin handling

    The `${stdin}` shared store variable has been removed. You must now explicitly
    mark an input parameter to receive piped data.

    <CodeGroup>
      ```yaml Before theme={null}
      ### process
      - type: shell
      - stdin: ${stdin}
      ```

      ```yaml After theme={null}
      ### input_name
      - type: string
      - stdin: true  # Routes pipe here
      ```
    </CodeGroup>

    ### CLI changes

    * `pflow workflow save` no longer accepts `--description`. It extracts the
      description from the Markdown content (text after the H1 header).
    * Metadata is now stored in YAML frontmatter rather than a `rich_metadata` wrapper.
  </Accordion>
</Update>

<Update label="January 2026" description="v0.5.0" tags={["New releases", "Improvements"]}>
  ## Batch processing

  Need to classify 50 commits with an LLM, or fetch 200 URLs? Add a `batch`
  config to any node and pflow handles the fan-out. Works with every node
  type — LLM, shell, HTTP, MCP, all of them.

  ```yaml theme={null}
  ### classify-items

  Classify each item in parallel. 30 concurrent LLM calls.

  - type: llm
  - batch:
      items: ${fetch-data.result}
      parallel: true
      max_concurrent: 30
  ```

  <Tip>
    Results stay in input order even in parallel mode, and each result
    includes the original item — so downstream nodes can always correlate
    outputs back to inputs without extra bookkeeping.
  </Tip>

  **Highlights**

  * Sequential and parallel execution with configurable concurrency (`max_concurrent`).
  * `error_handling: continue` keeps going when individual items fail — you get partial results instead of nothing.
  * Progress indicators in the CLI so you can see where a 200-item batch is at.
  * Access results with `${node.results}`, individual items with `${node.results[0].response}`.

  ## Smarter templates

  Template variables like `${node.stdout.items[0].name}` now parse JSON
  automatically. If a shell command outputs a JSON string, you can access
  nested fields directly — no more `jq` extraction steps between every shell
  node and the thing that consumes it.

  **Highlights**

  * `${node.stdout.field}` resolves through JSON strings without an intermediate node.
  * Inline object templates preserve types correctly — no more double-serialization when passing dicts.
  * Dicts and lists auto-coerce to JSON strings when mapped to string-typed parameters.
  * Optional inputs without defaults resolve correctly instead of erroring.

  <Accordion title="Before and after">
    Previously you needed an extraction step between a shell command and
    anything that wanted its output as structured data:

    <CodeGroup>
      ```yaml Before (v0.4.0) theme={null}
      ### get-data
      - type: shell
      # curl outputs JSON string

      ### extract-json
      - type: shell
      # jq extracts the field you need

      ### use-data
      - type: llm
      # finally gets the value
      ```

      ```yaml After (v0.5.0) theme={null}
      ### get-data
      - type: shell
      # curl outputs JSON string

      ### use-data
      - type: llm
      # access nested fields directly:
      # ${get-data.stdout.items[0].name}
      ```
    </CodeGroup>
  </Accordion>

  ## Shell node fixes

  Shell nodes now surface `stderr` even when the exit code is zero. Tools
  like `curl` and `ffmpeg` write diagnostics to stderr on success, and those
  warnings were getting lost.

  **Highlights**

  * `stderr` visible on successful commands, not just failures.
  * Trailing newlines stripped from `stdout` by default (disable with `strip_newline: false`).
  * Pipeline-aware error detection for `grep | sed` chains where only the last exit code was visible.
  * Fixed `SIGPIPE` crashes when a subprocess closed its input early.

  <Accordion title="Breaking changes">
    ### Explicit data wiring

    Nodes can no longer silently read from the shared store by key name. All
    data must be wired through `${variable}` templates. This prevents a class
    of bugs where a node ID collided with a parameter name and got the wrong
    value.

    <CodeGroup>
      ```yaml Before theme={null}
      ### transform
      # Could silently read "data" from shared store
      - type: code
      ```

      ```yaml After theme={null}
      ### transform
      # Must declare all inputs explicitly
      - type: code
      - inputs:
          data: ${fetch-data.response}
      ```
    </CodeGroup>

    ### Claude Code node

    * `task` → `prompt`
    * `working_directory` → `cwd`
    * `context` removed — include it directly in the prompt
  </Accordion>
</Update>

<Update label="December 2025" description="v0.4.0" tags={["Improvements", "Bug fixes"]}>
  ## Validation that helps you fix things

  When something goes wrong, pflow now tells the agent exactly what to do
  instead of printing a stack trace. Wrong template path? It shows every
  available output with its type and suggests the correct one.

  ```
  ✗ Node 'format-report' references unknown output 'fetch-data.email'

  Available outputs from 'fetch-data':
    ✓ ${fetch-data.response} (dict)
    ✓ ${fetch-data.status_code} (int)
    ✓ ${fetch-data.response_headers} (dict)

  Tip: Did you mean ${fetch-data.response}?
  ```

  <Note>
    Validation runs automatically before every execution — no separate step
    needed. The `--validate-only` flag lets agents check a workflow without
    running it.
  </Note>

  **Highlights**

  * Template references checked against actual node outputs before execution starts.
  * "Did you mean?" suggestions for misspelled node names and output paths.
  * Type mismatch warnings when connecting incompatible outputs to inputs.
  * `--validate-only` flag for CI pipelines and agent pre-checks.

  ## Agent tooling

  The CLI now has discovery commands so agents can find the right building
  blocks without knowing what's available ahead of time. `registry discover`
  takes a natural language description and returns matching nodes.

  **Highlights**

  * `pflow registry discover "fetch API data and send to Slack"` returns matching nodes ranked by relevance.
  * `pflow registry run node-type param=value` tests individual nodes outside of a workflow — output is pre-filtered for agents, showing structure without data.
  * `pflow instructions usage` gives agents a complete guide to pflow's commands and patterns.
  * Allow/deny filtering via `pflow settings` to control which nodes are available.

  <Accordion title="Example: agent discovery flow">
    ```bash theme={null}
    # Agent gets a task from the user
    # Step 1: Check if a workflow already exists
    pflow workflow discover "analyze git commits and post to slack"

    # Step 2: No match — find building blocks
    pflow registry discover "git log, LLM classification, slack message"

    # Step 3: Check a node's output structure
    pflow registry run mcp-composio-slack-SLACK_SEND_MESSAGE \
      channel="test" markdown_text="hello"

    # Step 4: Build the workflow using discovered nodes
    ```
  </Accordion>

  ## MCP server improvements

  Connecting external tools got more reliable. Server configs now expand
  environment variables everywhere (URLs, headers, auth fields), and sync
  only runs when something actually changed.

  **Highlights**

  * Environment variables expanded in all MCP config fields, not just API keys.
  * Smart sync skips re-scanning when server configs haven't changed (\~500ms saved on warm starts).
  * HTTP transport support for remote MCP servers alongside stdio.
  * Better error messages when MCP servers fail to start or authenticate.
</Update>

<Update label="November 2025" description="v0.3.0" tags={["New releases"]}>
  ## Workflow engine

  Write a `.pflow.md` file, run it from the terminal. Steps execute top to
  bottom, data flows between them through template variables. Save it with
  `pflow workflow save` and it becomes a command you can run from anywhere.

  ````markdown theme={null}
  ### fetch-users

  Get active users from the API.

  - type: http
  - url: https://api.example.com/users?status=active

  ### filter-active

  Keep only users who logged in this month.

  - type: code
  - inputs:
      users: ${fetch-users.response.data}

  ```python code
  users: list

  result: list = [u for u in users if u['last_login_days'] < 30]
  ```

  ### summarize

  Summarize the filtered users for the weekly report.

  - type: llm

  ```prompt
  Summarize these ${filter-active.result} active users for a weekly report.
  ```
  ````

  **Highlights**

  * Run from file path (`pflow workflow.pflow.md`) or by name (`pflow my-workflow`).
  * Templates reach into nested objects and arrays — `${node.result.data.users[0].email}`.
  * Execution traces saved to `~/.pflow/debug/` with per-node inputs, outputs, and timing.
  * Pipe workflows together: `pflow -p workflow-a | pflow -p workflow-b`.

  ## Built-in nodes

  Eight node types that cover the common building blocks. MCP bridges to
  anything else — GitHub, Slack, databases, whatever has an MCP server.

  **Highlights**

  * `shell` — run commands with dangerous-pattern blocking and timeouts.
  * `code` — inline Python with native object passing (no serialization overhead).
  * `llm` — any model via Simon Willison's [llm](https://llm.datasette.io/) library, with token tracking.
  * `http` — all methods, auth, request bodies, automatic JSON parsing.
  * `file` — read, write, copy, move, delete.
  * `mcp` — bridge to any MCP server over stdio or HTTP transport.
  * `claude-code` — delegate agentic subtasks to Claude Code.
  * `git` / `github` — common operations without shell scripting.

  ## MCP server

  pflow itself runs as an MCP server, so agents in Claude Desktop, Cursor,
  or any MCP-compatible environment can build and run workflows
  programmatically.

  **Highlights**

  * 11 tools covering workflow execution, node discovery, and registry inspection.
  * Structure-only output mode — agents see schema types without actual data, keeping context windows small.
  * Works alongside CLI usage. Same workflows, same registry, different interface.

  <Accordion title="Quick start">
    <Steps>
      <Step title="Install pflow">
        ```bash theme={null}
        uv tool install pflow-cli
        ```
      </Step>

      <Step title="Set up an LLM provider">
        ```bash theme={null}
        pflow settings set-env ANTHROPIC_API_KEY "sk-ant-..."
        ```
      </Step>

      <Step title="Run your first workflow">
        ```bash theme={null}
        pflow workflow.pflow.md
        ```

        Or tell your agent to run `pflow instructions usage` — it gets
        everything it needs to discover, build, and run workflows.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="What's next">
    Batch processing for fan-out patterns, smarter template resolution, and
    shell node reliability improvements. See the [Roadmap](/roadmap).
  </Accordion>
</Update>
