The Plan Tour
The Plan Tour: hand any orchestrator to the guide and it narrates the plan as prose - first this, then that, meanwhile the other - read straight from graph[:order] and graph[:edges]. If the prose sounds wrong, your plan IS wrong, and you found out before running it.
Plans & Graphs
Round 6
Matz
exit 0
bundle exec ruby examples/plan_tour.rb
a real captured run
THE PLAN, AS PROSE (nobody has cooked anything yet) First, boil the kettle. Meanwhile, slice the bread. After "boil the kettle": soft-boil the eggs. After "boil the kettle": steep the tea. After "slice the bread": toast the bread. After "soft-boil the eggs" (your protein), "toast the bread" (your crunch) and "steep the tea" (your comfort): plate everything. the narration is generated from graph[:order] and graph[:edges] - the same topology the scheduler will execute. read your plan aloud before you run it; ears catch what eyes skim.
source
# frozen_string_literal: true # The Plan Tour: hand any orchestrator to the guide and it narrates the # plan as prose - first this, then that, meanwhile the other - read # straight from graph[:order] and graph[:edges]. If the prose sounds # wrong, your plan IS wrong, and you found out before running it. # # bundle exec ruby examples/plan_tour.rb # # Runs offline; narration only, no execution. require class="s">"bundler/setup" require class="s">"agentic" # A breakfast, planned properly orchestrator = Agentic:class="y">:PlanOrchestrator.new def step(name) Agentic:class="y">:Task.new(description: name, agent_spec: {class="s">"name" => name, class="s">"instructions" => class="s">"cook"}) end kettle = step(class="s">"boil the kettle") eggs = step(class="s">"soft-boil the eggs") bread = step(class="s">"slice the bread") toast = step(class="s">"toast the bread") tea = step(class="s">"steep the tea") plate = step(class="s">"plate everything") orchestrator.add_task(kettle) orchestrator.add_task(bread) orchestrator.add_task(eggs, [kettle]) orchestrator.add_task(tea, [kettle]) orchestrator.add_task(toast, [bread]) orchestrator.add_task(plate, needs: {protein: eggs, crunch: toast, comfort: tea}) # --- the guide: graph in, prose out ------------------------------------------ def narrate(graph) names = graph[class="y">:tasks].transform_values(&class="y">:description) incoming = graph[class="y">:edges].group_by { |e| e[class="y">:to] } narrated = [] sentences = [] graph[class="y">:order].each do |task_id| edges_in = incoming[task_id] || [] sentence = if edges_in.empty? opener = narrated.empty? ? class="s">"First" : class="s">"Meanwhile" class="s">"#{opener}, #{names[task_id]}." else reasons = edges_in.map { |e| e[class="y">:label] ? class="s">"\"#{names[e[class="y">:from]]}\class="s">" (your #{e[class="y">:label]})" : class="s">"\"#{names[e[class="y">:from]]}\class="s">"" } joined = (reasons.size > 1) ? reasons[0..-2].join(class="s">", ") + class="s">" and #{reasons.last}" : reasons.first class="s">"After #{joined}: #{names[task_id]}." end sentences << sentence narrated << task_id end sentences end puts class="s">"THE PLAN, AS PROSE (nobody has cooked anything yet)" puts narrate(orchestrator.graph).each { |sentence| puts class="s">" #{sentence}" } puts puts class="s">"the narration is generated from graph[class="y">:order] and graph[class="y">:edges] -" puts class="s">"the same topology the scheduler will execute. read your plan aloud" puts class="s">"before you run it; ears catch what eyes skim."