Severity: low
Found in: 2026-07-22 code review
Location
lib/dag/workflow/definition.rb:40-47, lib/dag/workflow/definition/builder.rb:27-34; contrast with lib/dag/graph.rb:74 where edge metadata is eagerly checked via DAG.json_safe!.
Failure mode
The documented contract is "config [Hash] JSON-safe step config" (configs flow into to_h and therefore into the fingerprint port), but neither add_node path validates it. A config like {when: Time.now} is accepted, deep-frozen, and can even be durably stored by Memory::Storage; it only explodes later when the definition is fingerprinted (Stdlib::Fingerprint → ArgumentError: non JSON-safe value at $root.nodes[0].config.when) or serialized by a durable adapter. Boundary-validation inconsistency: edge metadata fails fast at the exact call site, node config does not.
Minimal trigger (verified)
DAG::Workflow::Definition.new.add_node(:a, type: :noop, config: {when: Time.now}) succeeds; .fingerprint(via: DAG::Adapters::Stdlib::Fingerprint.new) raises ArgumentError.
Fix direction
Call DAG.json_safe! on config: in both add_node paths, matching the edge-metadata behavior.
Severity: low
Found in: 2026-07-22 code review
Location
lib/dag/workflow/definition.rb:40-47,lib/dag/workflow/definition/builder.rb:27-34; contrast withlib/dag/graph.rb:74where edge metadata is eagerly checked viaDAG.json_safe!.Failure mode
The documented contract is "config [Hash] JSON-safe step config" (configs flow into
to_hand therefore into the fingerprint port), but neitheradd_nodepath validates it. A config like{when: Time.now}is accepted, deep-frozen, and can even be durably stored byMemory::Storage; it only explodes later when the definition is fingerprinted (Stdlib::Fingerprint→ArgumentError: non JSON-safe value at $root.nodes[0].config.when) or serialized by a durable adapter. Boundary-validation inconsistency: edge metadata fails fast at the exact call site, node config does not.Minimal trigger (verified)
DAG::Workflow::Definition.new.add_node(:a, type: :noop, config: {when: Time.now})succeeds;.fingerprint(via: DAG::Adapters::Stdlib::Fingerprint.new)raisesArgumentError.Fix direction
Call
DAG.json_safe!onconfig:in bothadd_nodepaths, matching the edge-metadata behavior.