The Hill Chart
The Hill Chart: Basecamp's answer to "how's it going?" - work climbs the hill while it's still uncertain (queued, waiting on dependencies) and rolls down once it's just execution. Three live snapshots of a running plan, drawn from lifecycle hooks. No status meeting convened.
Scheduling & Concurrency
Round 8
DHH
exit 0
bundle exec ruby examples/hill_chart.rb
a real captured run
THE HILL CHART (uphill = still uncertain, downhill = just execution)
early:
___________A
____/ \____
BC____/DEF \____
____/ \____
____/ \____
mid-flight:
___________CD
____/ \____
EF____/ \____ AB
____/ \____
____/ \____
at the end:
___________
____/ \____
____/ \____ ABCDEF
____/ \____
____/ \____
legend: A=audit copy, B=build hero, C=cut video, D=draft email, E=embed video, F=final review
the crest is the honest divider: left of it, tasks are waiting on
dependencies or a slot (uncertainty you can't schedule away);
right of it, it's just execution. the chart never asks anyone
'percent complete?' - the states are facts from hooks.
source
# frozen_string_literal: true # The Hill Chart: Basecamp's answer to "how's it going?" - work climbs # the hill while it's still uncertain (queued, waiting on dependencies) # and rolls down once it's just execution. Three live snapshots of a # running plan, drawn from lifecycle hooks. No status meeting convened. # # bundle exec ruby examples/hill_chart.rb # # Runs offline; watch the letters roll downhill. require class="s">"bundler/setup" require class="s">"agentic" WORK = { class="s">"A: audit copy" => {sleep: 0.05, deps: []}, class="s">"B: build hero" => {sleep: 0.09, deps: []}, class="s">"C: cut video" => {sleep: 0.12, deps: []}, class="s">"D: draft email" => {sleep: 0.06, deps: [class="s">"A: audit copy"]}, class="s">"E: embed video" => {sleep: 0.05, deps: [class="s">"B: build hero", class="s">"C: cut video"]}, class="s">"F: final review" => {sleep: 0.04, deps: [class="s">"D: draft email", class="s">"E: embed video"]} }.freeze # Position on the hill, 0.0 (left base) to 1.0 (right base) POSITIONS = {pending: 0.15, queued: 0.35, running: 0.55, done: 0.9}.freeze states = WORK.keys.to_h { |name| [name, class="y">:pending] } snapshots = [] take_snapshot = -> { snapshots << states.dup } hooks = { before_task_execution: ->(task_id:, task:) { states[task.description] = class="y">:queued }, task_slot_acquired: ->(task_id:, task:, waited:) { states[task.description] = class="y">:running take_snapshot.call }, after_task_success: ->(task_id:, task:, result:, duration:) { states[task.description] = class="y">:done } } orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 2, lifecycle_hooks: hooks) tasks = {} WORK.each do |name, spec| tasks[name] = Agentic:class="y">:Task.new(description: name, agent_spec: {class="s">"name" => name, class="s">"instructions" => class="s">"work"}, payload: spec[class="y">:sleep]) orchestrator.add_task(tasks[name], spec[class="y">:deps].map { |d| tasks.fetch(d) }, agent: ->(t) { sleep(t.payload) || class="y">:ok }) end orchestrator.execute_plan take_snapshot.call # the finished hill # --- draw the hill ------------------------------------------------------------- HILL = [ class="s">" ___________ ", class="s">" ____/ \\____ ", class="s">" ____/ \\____ ", class="s">" ____/ \\____ ", class="s">"____/ \\____" ].freeze def draw_hill(states) width = HILL.first.length rows = HILL.map(&class="y">:dup) # Height of the hill surface at each column, from the art itself surface = (0...width).map { |col| rows.index { |row| row[col] != class="s">" " } || rows.size - 1 } states.each do |name, state| col = (POSITIONS.fetch(state) * (width - 1)).round row = [surface[col] - 1, 0].max letter = name[0] col += 1 while rows[row][col] != class="s">" " && col < width - 1 rows[row][col] = letter end rows.each { |row| puts class="s">" #{row}" } end puts class="s">"THE HILL CHART (uphill = still uncertain, downhill = just execution)" [0, snapshots.size / 2, snapshots.size - 1].uniq.each_with_index do |index, i| snap = snapshots[index] puts puts class="s">" #{["early:class="s">", "mid-flight:class="s">", "at the end:class="s">"][i]}" draw_hill(snap) end puts puts class="s">" legend: #{WORK.keys.map { |n| n.split(":class="s">").first + "=class="s">" + n.split(": class="s">").last }.join(", class="s">")}" puts puts class="s">"the crest is the honest divider: left of it, tasks are waiting on" puts class="s">"dependencies or a slot (uncertainty you can't schedule away);" puts class="s">"right of it, it's just execution. the chart never asks anyone" puts class="s">"'percent complete?' - the states are facts from hooks."