agentic examples

Status Board

Status Board: output is not always text. The wallboard your team glances at is a FILE - an SVG chart, a CSV you can pivot, a JSON summary a dashboard ingests - and a plan is the right machine to produce one: collect (aggregate raw timings), render (draw the SVG), export (write CSV + JSON), each step checkable, the artifacts the deliverable. Examples honor AGENTIC_ARTIFACTS_DIR so the showcase can collect and display what a run produces; without it they write to a tmpdir like …

Data & Pipelines exit 0

source on github

bundle exec ruby examples/status_board.rb

a real captured run

STATUS BOARD (the deliverable is files, the plan is the factory)

  plan status: completed; artifacts in /home/runner/work/agentic-examples/agentic-examples/docs/artifacts/status_board:
    status_board.csv   145 bytes
    status_board.svg   1149 bytes
    summary.json       71 bytes

  the referee doesn't trust the plan's word - it reopens the files:
  6 bars for 6 suites, the CSV re-adds to the
  JSON's 509.1s, and "system" is slowest, as anyone who has
  run a system suite already knew. artifacts you can open beat
  claims you have to believe.

artifacts this run produced

source

# frozen_string_literal: true

# Status Board: output is not always text. The wallboard your team
# glances at is a FILE - an SVG chart, a CSV you can pivot, a JSON
# summary a dashboard ingests - and a plan is the right machine to
# produce one: collect (aggregate raw timings), render (draw the SVG),
# export (write CSV + JSON), each step checkable, the artifacts the
# deliverable. Examples honor AGENTIC_ARTIFACTS_DIR so the showcase
# can collect and display what a run produces; without it they write
# to a tmpdir like polite guests.
#
#   bundle exec ruby examples/status_board.rb
#
# Runs offline; exits 1 unless the SVG holds one bar per suite, the
# CSV re-adds to the JSON's totals, and every artifact is non-empty.

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

Agentic.logger.level = class="y">:fatal

OUT = ENV[class="s">"AGENTIC_ARTIFACTS_DIR"] || Dir.mktmpdir(class="s">"status_board")
Dir.mkdir(OUT) unless Dir.exist?(OUT)

# Two weeks of CI timings (seconds), deterministic world:
rng = Random.new(42)
SUITES = %w[models requests jobs mailers system lint].freeze
TIMINGS = SUITES.to_h { |s| [s, Array.new(14) { 40 + rng.rand(60) + ((s == class="s">"system") ? 90 : 0) }] }

collect = Agentic:class="y">:Task.new(description: class="s">"aggregate suite timings",
  agent_spec: {class="s">"name" => class="s">"collector", class="s">"instructions" => class="s">"aggregate"})
render = Agentic:class="y">:Task.new(description: class="s">"render the SVG wallboard",
  agent_spec: {class="s">"name" => class="s">"chartist", class="s">"instructions" => class="s">"draw"})
export = Agentic:class="y">:Task.new(description: class="s">"export CSV + JSON",
  agent_spec: {class="s">"name" => class="s">"exporter", class="s">"instructions" => class="s">"write"})

stats = nil
orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 1, retry_policy: {max_retries: 0, retryable_errors: []})
orchestrator.add_task(collect, [], agent: ->(_t) {
  stats = SUITES.map { |s|
    d = TIMINGS[s]
    {suite: s, mean: (d.sum.to_f / d.size).round(1), max: d.max, runs: d.size}
  }
  class="y">:aggregated
})
orchestrator.add_task(render, [collect], agent: ->(_t) {
  w, bar_h, gap, left = 460, 22, 8, 90
  peak = stats.map { |r| r[class="y">:mean] }.max
  bars = stats.each_with_index.map { |r, i|
    y = 10 + i * (bar_h + gap)
    len = ((w - left - 60) * r[class="y">:mean] / peak).round
    %(<text x=class="s">"#{left - 6}" y=class="s">"#{y + 15}" text-anchor=class="s">"end" font-size=class="s">"12">#{r[class="y">:suite]}</text>) +
      %(<rect x=class="s">"#{left}" y=class="s">"#{y}" width=class="s">"#{len}" height=class="s">"#{bar_h}" fill=class="s">"#4c78a8"/>) +
      %(<text x=class="s">"#{left + len + 6}" y=class="s">"#{y + 15}" font-size=class="s">"11">#{r[class="y">:mean]}s</text>)
  }.join
  h = 20 + stats.size * (bar_h + gap)
  File.write(File.join(OUT, class="s">"status_board.svg"),
    %(<svg xmlns=class="s">"http://www.w3.org/2000/svg" width=class="s">"#{w}" height=class="s">"#{h}" font-family=class="s">"sans-serif">#{bars}</svg>))
  class="y">:rendered
})
orchestrator.add_task(export, [render], agent: ->(_t) {
  File.write(File.join(OUT, class="s">"status_board.csv"),
    class="s">"suite,mean_seconds,max_seconds,runs\n" + stats.map { |r| class="s">"#{r[class="y">:suite]},#{r[class="y">:mean]},#{r[class="y">:max]},#{r[class="y">:runs]}" }.join(class="s">"\n") + class="s">"\n")
  File.write(File.join(OUT, class="s">"summary.json"),
    JSON.pretty_generate({suites: stats.size, total_mean_seconds: stats.sum { |r| r[class="y">:mean] }.round(1), slowest: stats.max_by { |r| r[class="y">:mean] }[class="y">:suite]}))
  class="y">:exported
})
status = orchestrator.execute_plan.status

puts class="s">"STATUS BOARD (the deliverable is files, the plan is the factory)"
puts
puts class="s">"  plan status: #{status}; artifacts in #{OUT}:"
Dir[File.join(OUT, class="s">"*")].sort.each { |f| puts class="s">"    #{File.basename(f).ljust(18)} #{File.size(f)} bytes" }
puts

svg = File.read(File.join(OUT, class="s">"status_board.svg"), encoding: class="s">"UTF-8")
csv = File.readlines(File.join(OUT, class="s">"status_board.csv"))
summary = JSON.parse(File.read(File.join(OUT, class="s">"summary.json")))
csv_total = csv.drop(1).sum { |l| l.split(class="s">",")[1].to_f }.round(1)

failures = []
failures << class="s">"plan status: #{status}" unless status == class="y">:completed
failures << class="s">"SVG bars != suites" unless svg.scan(class="s">"<rect").size == SUITES.size
failures << class="s">"CSV rows != suites" unless csv.size == SUITES.size + 1
failures << class="s">"JSON total #{summary["total_mean_secondsclass="s">"]} != CSV re-sum #{csv_total}" unless summary[class="s">"total_mean_seconds"] == csv_total
failures << class="s">"an artifact is empty" if Dir[File.join(OUT, class="s">"*")].any? { |f| File.zero?(f) }
failures << class="s">"slowest suite should be system" unless summary[class="s">"slowest"] == class="s">"system"

puts class="s">"  the referee doesn't trust the plan's word - it reopens the files:"
puts class="s">"  #{svg.scan("<rectclass="s">").size} bars for #{SUITES.size} suites, the CSV re-adds to the"
puts class="s">"  JSON's #{summary["total_mean_secondsclass="s">"]}s, and \"#{summary[class="s">"slowest"]}\class="s">" is slowest, as anyone who has"
puts class="s">"  run a system suite already knew. artifacts you can open beat"
puts class="s">"  claims you have to believe."
exit(failures.empty? ? 0 : 1)