Changelog

March 2026

Added

  • Checkpoint lineage exceptionsWorkflowAlreadyCompletedError, GraphChangedError, WorkflowForkError, and InputOverrideRequiresForkError for explicit resume/fork guidance.

  • checkpoint= on runner.run() — explicit fork entrypoint from a saved checkpoint snapshot (values + steps).

  • First-class fork/retry helpers — low-level SqliteCheckpointer.fork_workflow() / retry_workflow() (sync) and fork_workflow_async() / retry_workflow_async() (async) prepare lineage-aware checkpoints when you need manual control.

  • Graph structural_hash — structure-level compatibility hash used to guard same-workflow_id resumes.

  • graph.describe() — compact human-readable graph summary covering scoped inputs, bound values, outputs, and active nodes. Type hints are shown by default, with show_types=False for name-only output.

  • Run lineage metadata — persisted forked_from, fork_superstep, retry_of, retry_index fields on runs.

  • Gate decision value persistence — gates now emit internal _gate_name values so routing intent is checkpoint-visible and reconstructible.

  • Auto-generated workflow IDs for run() — when a checkpointer is configured and workflow_id is omitted.

  • override_workflow on run() — convenience auto-fork mode: when a workflow_id already exists, pass override_workflow=True to branch to a fresh lineage instead of raising strict resume errors.

  • Workflow-id based forking/retrying on run()fork_from= and retry_from= remove manual checkpoint plumbing for common branch/retry flows.

  • Checkpointer naming convention updated — sync helpers are now fork_workflow() / retry_workflow(), async helpers are fork_workflow_async() / retry_workflow_async().

  • Persisted paused workflows — checkpointers now store interrupt-paused runs as PAUSED instead of overloading ACTIVE, and CLI/dashboard views expose paused runs distinctly.

Changed

  • Resume contract is strict — same workflow_id now means same lineage only:

    • completed workflows are terminal

    • runtime input overrides require explicit fork

    • structural graph changes require fork

  • Checkpoint state reconstruction — restore now replays version counters from step history (instead of remap-style flattening), improving cycle correctness and stale-node detection.

  • Unified startup scheduling — first-run readiness is predecessor-driven for both implicit and explicit edge graphs (no split behavior by edge mode).

  • Canonical graph scope — execution scope is graph-configured (with_entrypoint, select, bind) and shared by scheduler, validation, and visualization.

  • Runtime scope overrides removed — passing runtime select= or entrypoint= to runners now raises ValueError. Configure scope on the graph instance instead.

  • Cycles require constructor entrypoint — constructing a cyclic graph without Graph(..., entrypoint=...) now raises GraphConfigError.

  • Internal output injection tightened — user-provided values for edge-produced internal parameters are rejected deterministically.

  • Visualization defaults simplified.visualize() now shows unbound external inputs by default via show_inputs=True, keeps bound inputs hidden unless show_bounded_inputs=True, and never renders shared params as external inputs.

  • Visualization edge contract tightened — rendered views now follow the Python-precomputed edge set (no JS-only transitive pruning), so what you see matches the canonical NetworkX topology.

  • Implicit producer shadow-elimination — for contested input p, edge u -> v (p) is removed iff every valid path from u to v for p crosses another producer of p first; unresolved cases raise GraphConfigError at build time.

Recent Merged PRs

PR #61arrow-up-right (Merged February 28, 2026) - DiskCache HMAC integrity hardening

  • Added HMAC-SHA256 signing for DiskCache payloads using a per-cache-directory secret key.

  • DiskCache.get() now verifies HMAC integrity before deserialization (pickle.loads), preventing untrusted/tampered payload deserialization.

  • Added atomic HMAC key initialization (O_CREAT | O_EXCL) to avoid race conditions when multiple processes initialize the same cache directory.

  • Added broader recovery behavior for corrupted or legacy cache entries:

    • non-bytes payloads are evicted and treated as cache misses

    • missing/invalid HMAC metadata is evicted and treated as cache misses

    • deserialization failures evict the bad entry and return miss

  • Expanded disk cache integrity tests to cover tampering, key initialization races, and bad metadata handling.

PR #59arrow-up-right (Merged February 26, 2026) - Non-TTY progress fallback

  • Added non-TTY mode to RichProgressProcessor for CI/piped environments where live Rich bars are not appropriate.

  • Added milestone-based map progress logging at 10%, 25%, 50%, 75%, and 100%.

  • Added explicit mode control via RichProgressProcessor(force_mode="auto" | "tty" | "non-tty").

  • Added non-TTY node start/end/failure plain-text logging for non-map runs.

  • Cleaned up non-TTY tracking internals after review (removed unused fields and simplified state).

  • Added dedicated tests for non-TTY behavior.

PR #58arrow-up-right (Merged February 25, 2026) - PR workflow and contributor docs updates

  • Added .github/PULL_REQUEST_TEMPLATE.md with a problem + before/after structure.

  • Updated internal skills and contributor instructions to reuse the PR template and reduce duplicated guidance.

  • Removed legacy "entire session" tracking guidance from project docs/instructions.

February 2026

Added

  • with_entrypoint(*node_names) — Graph method that narrows execution to start at specific nodes, skipping upstream. Works for DAGs and cycles. Returns a new Graph (immutable, chainable). Upstream nodes are excluded from the active set and never execute at runtime.

  • Select-aware InputSpecgraph.select("a").inputs.required now shows only what's needed to produce output "a", not the full graph. Previously select() only filtered returned outputs.

  • Runtime select overrides removedrunner.run(..., select=...) is no longer supported. Configure output scope on the graph with graph.select(...) instead.

  • entrypoints_config propertygraph.entrypoints_config returns the configured entry point node names, or None if all nodes are active.

Changed

  • InputSpec is now scope-awaregraph.inputs considers both with_entrypoint() (forward-reachable) and select() (backward-reachable) when determining required, optional, and entrypoint parameters. Parameters from excluded nodes no longer appear in InputSpec.

January 2026

Added

  • Event systemRunStartEvent, NodeStartEvent, NodeEndEvent, and other event types emitted during execution. Pass event_processors=[...] to runner.run() or runner.map() to observe execution

  • RichProgressProcessor — hierarchical Rich progress bars with failed item tracking for map() operations

  • InterruptNode — human-in-the-loop pause/resume support for async workflows

  • RouteNode & @route decorator — conditional control flow gates with build-time target validation

  • IfElseNode & @ifelse decorator — binary boolean routing for simple branching

  • Error handling in map()error_handling parameter for runner.map() and GraphNode.map_over() with partial result support

  • SyncRunner & AsyncRunner — full execution runtime with superstep-based scheduling, concurrency support, and global max_concurrency

  • GraphNode.map_over() — run a nested graph over a collection of inputs

  • Type validationstrict_types parameter on Graph with a full type compatibility engine supporting generics, Annotated, and forward refs

  • select() method — default output selection for graphs

  • Mutex branch support — allow same output names in mutually exclusive branches

  • Sole Producer Rule — prevents self-retriggering in cyclic graphs

  • Capability test matrix — pairwise combination testing with renaming and binding dimensions

  • Comprehensive documentation — getting started guide, routing patterns, API reference, philosophy, and how-to guides

Changed

  • Refactored event context — pass event context as params instead of mutable executor state

  • Refactored runners — separated structural and execution layers

  • Refactored routing — extracted shared validation to common module

  • Refactored graph — extracted validation and input_spec into graph/ package

  • Renamed with_select() to select() for clearer API semantics

  • Renamed inputs= to values= parameter in runner API

Fixed

  • Bound values no longer deep-copied — nested graphs with bound non-copyable objects (e.g., embedders with thread locks) now work correctly. Bound values are intentionally shared (not copied), matching dependency injection patterns. Non-copyable signature defaults now raise GraphConfigError with helpful guidance to use .bind() instead

  • Preserve partial state from same-superstep nodes on failure

  • Support multiple values per edge in graph data model

  • Deduplicate Graph.outputs for mutex branches

  • Reject string 'END' as target name to avoid confusion with END sentinel

  • Python keyword validation in node names

  • Literal type forward ref resolution

  • Generic type arity check enforcement

  • Renamed input/output translation in map_over execution

Last updated