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
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
- status_board.csv · 145 bytes
status_board.svg · 1149 bytes- summary.json · 71 bytes
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)