Skip to content

Mutated Workflow IR should be directly evaluatable without config round-trip #1410

Description

@lambdabaa

Problem

apply_random_mutation() returns a mutated Workflow IR with changes applied (e.g., rewritten prompt_template on an AgentNode). But consumers that rebuild from a config object (like Package.compile()) lose these mutations because compile() always starts fresh from the Package's knobs/defaults.

This means PROMPT_MUTATE has no effect in any outer loop that follows the pattern:

child_wf, rec = apply_random_mutation(parent_wf, strategy, gen)
# ... extract knob overrides from child_wf.knob_values ...
child_pipeline = build_pipeline(cfg_with_overrides)  # rebuilds from config
result = evaluate(child_pipeline)  # prompt mutation is lost

The mutated prompt lives in child_wf.nodes[target].prompt_template but is never seen by the evaluator.

Impact

Discovered in the chess evolution demo: after 30+ generations with 50% PROMPT_MUTATE weight and a working default_prompt_rewriter (38/38 Opus rewrites succeeded), every prompt mutation was actually evaluated with the original prompt. The rewritten prompts were silently discarded.

This affects any outer loop consumer that doesn't evaluate the Workflow IR directly — which is the natural pattern when using Package ecosystem's compile().

Proposed fix

The outer loop should carry prompt (and other node-level) mutations through the evaluation path. Options:

  1. Workflow.apply_to(package: Package) -> Package — patches mutations from the IR back onto a Package, returning a new Package ready to compile+evaluate
  2. WorkflowExecutor accepts Workflow directly — skip the Package round-trip entirely for evaluation
  3. Store node-level mutations in knob_values — treat prompts as knobs so they survive the compile() round-trip (this is what OptKnob(kind="prompt") was designed for, but mutate_prompt bypasses it)

Option 3 is the most aligned with the existing design — PROMPT_MUTATE should write to knob_values when the target node has an associated OptKnob(kind="prompt"), so mutations survive compile().

References

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions