agentic examples

The Structural Diff

The Structural Diff: two versions of a plan's wire format, diffed as TOPOLOGY - tasks added and removed, edges rewired, labels renamed. A line diff of plan JSON tells you bytes changed; this tells you what changed about the plan.

Plans & Graphs Round 7 Xavier Noria exit 0

source on github

bundle exec ruby examples/plan_structural_diff.rb

a real captured run

PLAN STRUCTURAL DIFF (v1 -> v2)

  + task  dedupe entries
  + edge  parse entries -> dedupe entries
  + edge  dedupe entries -> rank entries
  - edge  parse entries -> rank entries

  4 structural changes. the review question is no longer
  'what do these 40 changed JSON lines mean' but 'should ranking
  consume deduped candidates instead of raw entries' - which is
  a question a human can actually answer.

source

# frozen_string_literal: true

# The Structural Diff: two versions of a plan's wire format, diffed as
# TOPOLOGY - tasks added and removed, edges rewired, labels renamed.
# A line diff of plan JSON tells you bytes changed; this tells you what
# changed about the plan.
#
#   bundle exec ruby examples/plan_structural_diff.rb
#
# Runs offline; v1 and v2 are built in-process via the round-trip wire
# format, as they would be loaded from two commits of plan.json.

require class="s">"bundler/setup"
require class="s">"agentic"

def step(name)
  Agentic:class="y">:Task.new(description: name, agent_spec: {class="s">"name" => name, class="s">"instructions" => class="s">"work"})
end

def wire(orchestrator)
  graph = orchestrator.graph
  names = graph[class="y">:tasks].transform_values(&class="y">:description)
  {
    class="s">"tasks" => graph[class="y">:order].map { |id| names[id] },
    class="s">"edges" => graph[class="y">:edges].map { |e|
      {class="s">"from" => names[e[class="y">:from]], class="s">"to" => names[e[class="y">:to]], class="s">"label" => e[class="y">:label]&.to_s}
    }
  }
end

# --- version 1: last sprint's pipeline ---------------------------------------
v1 = Agentic:class="y">:PlanOrchestrator.new
fetch = step(class="s">"fetch feed")
parse = step(class="s">"parse entries")
rank = step(class="s">"rank entries")
publish = step(class="s">"publish digest")
v1.add_task(fetch)
v1.add_task(parse, [fetch])
v1.add_task(rank, needs: {entries: parse})
v1.add_task(publish, [rank])

# --- version 2: this sprint's - dedupe added, ranking rewired ----------------
v2 = Agentic:class="y">:PlanOrchestrator.new
fetch2 = step(class="s">"fetch feed")
parse2 = step(class="s">"parse entries")
dedupe2 = step(class="s">"dedupe entries")
rank2 = step(class="s">"rank entries")
publish2 = step(class="s">"publish digest")
v2.add_task(fetch2)
v2.add_task(parse2, [fetch2])
v2.add_task(dedupe2, needs: {entries: parse2})
v2.add_task(rank2, needs: {candidates: dedupe2})
v2.add_task(publish2, [rank2])

# --- the diff: sets of names and labeled edges --------------------------------
def structural_diff(before, after)
  edge_key = ->(e) { [e[class="s">"from"], e[class="s">"to"]] }

  before_edges = before[class="s">"edges"].to_h { |e| [edge_key.call(e), e[class="s">"label"]] }
  after_edges = after[class="s">"edges"].to_h { |e| [edge_key.call(e), e[class="s">"label"]] }

  {
    tasks_added: after[class="s">"tasks"] - before[class="s">"tasks"],
    tasks_removed: before[class="s">"tasks"] - after[class="s">"tasks"],
    edges_added: (after_edges.keys - before_edges.keys),
    edges_removed: (before_edges.keys - after_edges.keys),
    labels_changed: before_edges.keys.intersection(after_edges.keys)
      .reject { |k| before_edges[k] == after_edges[k] }
      .map { |k| [k, before_edges[k], after_edges[k]] }
  }
end

diff = structural_diff(wire(v1), wire(v2))

puts class="s">"PLAN STRUCTURAL DIFF (v1 -> v2)"
puts
diff[class="y">:tasks_added].each { |t| puts class="s">"  + task  #{t}" }
diff[class="y">:tasks_removed].each { |t| puts class="s">"  - task  #{t}" }
diff[class="y">:edges_added].each { |(from, to)| puts class="s">"  + edge  #{from} -> #{to}" }
diff[class="y">:edges_removed].each { |(from, to)| puts class="s">"  - edge  #{from} -> #{to}" }
diff[class="y">:labels_changed].each { |(from, to), old, new| puts class="s">"  ~ label #{from} -> #{to}: #{old.inspect} => #{new.inspect}" }

puts
total = diff.values.sum(&class="y">:size)
puts class="s">"  #{total} structural changes. the review question is no longer"
puts class="s">"  'what do these 40 changed JSON lines mean' but 'should ranking"
puts class="s">"  consume deduped candidates instead of raw entries' - which is"
puts class="s">"  a question a human can actually answer."