Skip to content

Align context key-collision semantics between linear and DAG flows #337

@dgenio

Description

@dgenio

Summary

Align (or explicitly document and gate) the context key-collision rules between linear and DAG flows, which currently differ: linear flows silently overwrite earlier step outputs, while DAG flows reject same-level sibling collisions but allow level-to-level overwrites.

Why this matters

The accumulated execution context is the data plane of every flow. Silent overwrites mean a reordering or added step can drop data with no error and only a DEBUG-level log line — a subtle correctness trap. The asymmetry also means a flow's safety against collisions changes when it is converted between linear and DAG forms.

Current evidence

  • Linear merge (executor.py ~lines 1324–1332): key collisions log at DEBUG and context.update(record.outputs) overwrites.
  • DAG merge (executor.py ~lines 4024–4063): sibling collisions within a level abort the flow with FlowExecutionError, but level_outputs then overwrite prior context keys across levels (~line 4100).
  • The asymmetry is noted in code comments (~lines 3716–3717) but not surfaced to users in docs or at compile time.

Proposed implementation

  1. Choose a uniform policy. Recommended: a flow-level on_context_collision setting with values "overwrite" (current linear behavior), "error", and "warn"; default "warn" for one minor release, then consider "error" as the default in a later release.
  2. Implement the policy in one shared merge helper used by both linear and DAG paths (pairs with the sync/async consolidation issue).
  3. Keep DAG sibling-collision rejection unconditional (it is genuinely ambiguous), and apply the policy to sequential/level-to-level overwrites in both flow kinds.
  4. Surface predictable collisions statically where possible: compile_flow() can warn when two steps' output schemas share field names.
  5. Document the merge semantics in docs/data-integrity.md and AGENTS.md §5.

Acceptance criteria

  • One shared merge implementation governs both flow kinds.
  • on_context_collision="error" aborts with a typed error naming the step and key; "warn" logs at WARNING; "overwrite" preserves current behavior.
  • compile_flow emits a warning for statically detectable collisions.
  • Documented semantics match implemented behavior.

Test plan

  • New tests covering each policy value for linear and DAG flows, plus the compile-time warning.
  • Existing execution tests pass with the compatibility default.
  • Full validation commands pass.

Migration notes

Default change from silent overwrite to WARNING-level logging is observable but non-breaking. If the default later moves to "error", announce via deprecation notes one minor release ahead per docs/versioning-policy.md.

Risks and tradeoffs

  • Some legitimate flows intentionally shadow keys (e.g., refine-in-place pipelines); the "overwrite" opt-out keeps them working.
  • A new Flow field touches serialization/schema export; update schemas.py and round-trip tests accordingly.

Suggested labels

reliability, breaking-change, architecture

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions