agentic examples

The Plan Merge

The Plan Merge: base, ours, theirs - a three-way merge of plan wire formats. Independent changes combine; the same edge rewired two different ways is a CONFLICT, reported in topology vocabulary, not JSON-line vocabulary. Round 7 gave plans diff; this gives them merge.

Plans & Graphs Round 8 Xavier Noria exit 0

source on github

bundle exec ruby examples/plan_merge.rb

a real captured run

PLAN MERGE (base + ours + theirs)

  cleanly merged:
    tasks: fetch, parse, rank, publish, dedupe, moderate, audit
    + parse -> dedupe (entries)
    + dedupe -> rank (candidates)
    + parse -> moderate (entries)
    + moderate -> rank (safe_entries)
    + publish -> audit

  CONFLICTS (both branches rewired the same seam):
    seam parse -> rank:
      ours:   parse -> dedupe -> ...
      theirs: parse -> moderate -> ...

  resolution is a DESIGN decision - should dedupe run before
  moderation, after it, or fused? no textual merge can answer
  that, which is why the conflict is reported in topology terms:
  the humans must decide the order of the new stages.

source

# frozen_string_literal: true

# The Plan Merge: base, ours, theirs - a three-way merge of plan wire
# formats. Independent changes combine; the same edge rewired two
# different ways is a CONFLICT, reported in topology vocabulary, not
# JSON-line vocabulary. Round 7 gave plans diff; this gives them merge.
#
#   bundle exec ruby examples/plan_merge.rb
#
# Runs offline; two teammates edit the same pipeline.

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

# The wire format from plan_roundtrip: tasks + labeled edges
BASE = {
  class="s">"tasks" => [class="s">"fetch", class="s">"parse", class="s">"rank", class="s">"publish"],
  class="s">"edges" => [
    {class="s">"from" => class="s">"fetch", class="s">"to" => class="s">"parse", class="s">"label" => nil},
    {class="s">"from" => class="s">"parse", class="s">"to" => class="s">"rank", class="s">"label" => class="s">"entries"},
    {class="s">"from" => class="s">"rank", class="s">"to" => class="s">"publish", class="s">"label" => nil}
  ]
}.freeze

# Ours: adds dedupe between parse and rank
OURS = {
  class="s">"tasks" => [class="s">"fetch", class="s">"parse", class="s">"dedupe", class="s">"rank", class="s">"publish"],
  class="s">"edges" => [
    {class="s">"from" => class="s">"fetch", class="s">"to" => class="s">"parse", class="s">"label" => nil},
    {class="s">"from" => class="s">"parse", class="s">"to" => class="s">"dedupe", class="s">"label" => class="s">"entries"},
    {class="s">"from" => class="s">"dedupe", class="s">"to" => class="s">"rank", class="s">"label" => class="s">"candidates"},
    {class="s">"from" => class="s">"rank", class="s">"to" => class="s">"publish", class="s">"label" => nil}
  ]
}.freeze

# Theirs: adds moderation between parse and rank (same seam!)
# and independently adds an audit leaf off publish
THEIRS = {
  class="s">"tasks" => [class="s">"fetch", class="s">"parse", class="s">"moderate", class="s">"rank", class="s">"publish", class="s">"audit"],
  class="s">"edges" => [
    {class="s">"from" => class="s">"fetch", class="s">"to" => class="s">"parse", class="s">"label" => nil},
    {class="s">"from" => class="s">"parse", class="s">"to" => class="s">"moderate", class="s">"label" => class="s">"entries"},
    {class="s">"from" => class="s">"moderate", class="s">"to" => class="s">"rank", class="s">"label" => class="s">"safe_entries"},
    {class="s">"from" => class="s">"rank", class="s">"to" => class="s">"publish", class="s">"label" => nil},
    {class="s">"from" => class="s">"publish", class="s">"to" => class="s">"audit", class="s">"label" => nil}
  ]
}.freeze

def edge_map(wire)
  wire[class="s">"edges"].to_h { |e| [[e[class="s">"from"], e[class="s">"to"]], e[class="s">"label"]] }
end

def merge(base, ours, theirs)
  base_edges = edge_map(base)
  our_edges = edge_map(ours)
  their_edges = edge_map(theirs)

  conflicts = []
  merged_tasks = (base[class="s">"tasks"] | ours[class="s">"tasks"] | theirs[class="s">"tasks"])

  # An edge's fate in each branch: kept, removed, or added
  all_keys = (base_edges.keys | our_edges.keys | their_edges.keys)
  merged_edges = all_keys.filter_map do |key|
    in_base = base_edges.key?(key)
    in_ours = our_edges.key?(key)
    in_theirs = their_edges.key?(key)

    if in_base && !in_ours && !in_theirs
      # Both branches removed this edge - but did they replace it the
      # same way? If both rewired the same seam differently, conflict.
      our_replacement = our_edges.keys.find { |k| k[0] == key[0] && !base_edges.key?(k) }
      their_replacement = their_edges.keys.find { |k| k[0] == key[0] && !base_edges.key?(k) }
      if our_replacement && their_replacement && our_replacement != their_replacement
        conflicts << {seam: key, ours: our_replacement, theirs: their_replacement}
      end
      nil
    elsif in_base && in_ours && in_theirs
      [key, base_edges[key]] # unchanged everywhere
    elsif !in_base
      [key, (our_edges[key] || their_edges[key])] # added by one branch
    else
      [key, (in_ours ? our_edges[key] : their_edges[key])] # kept by one, removed by other -> keep? no: removed wins
    end
  end

  [{class="s">"tasks" => merged_tasks, class="s">"edges" => merged_edges.map { |(from, to), label|
    {class="s">"from" => from, class="s">"to" => to, class="s">"label" => label}
  }}, conflicts]
end

merged, conflicts = merge(BASE, OURS, THEIRS)

puts class="s">"PLAN MERGE (base + ours + theirs)"
puts
puts class="s">"  cleanly merged:"
puts class="s">"    tasks: #{merged["tasksclass="s">"].join(", class="s">")}"
(merged[class="s">"edges"] - BASE[class="s">"edges"]).each do |e|
  puts class="s">"    + #{e["fromclass="s">"]} -> #{e["toclass="s">"]}#{e["labelclass="s">"] ? " (#{e[class="s">"label"]})class="s">" : "class="s">"}"
end
puts
if conflicts.any?
  puts class="s">"  CONFLICTS (both branches rewired the same seam):"
  conflicts.each do |c|
    puts class="s">"    seam #{c[class="y">:seam][0]} -> #{c[class="y">:seam][1]}:"
    puts class="s">"      ours:   #{c[class="y">:seam][0]} -> #{c[class="y">:ours][1]} -> ..."
    puts class="s">"      theirs: #{c[class="y">:seam][0]} -> #{c[class="y">:theirs][1]} -> ..."
  end
  puts
  puts class="s">"  resolution is a DESIGN decision - should dedupe run before"
  puts class="s">"  moderation, after it, or fused? no textual merge can answer"
  puts class="s">"  that, which is why the conflict is reported in topology terms:"
  puts class="s">"  the humans must decide the order of the new stages."
end