The Gentle Deadline
The Gentle Deadline: most deadline code is violent - a timeout fires, everything dies, the user gets an error page at 30.0 seconds that could have been a good-enough answer at 29. This plan knows which of its tasks are ESSENTIAL and which are garnish, and when the time budget runs low it starts declining the garnish - politely, by name, with the meal still served on time.
Reliability & Recovery
Round 16
Yukihiro Matsumoto
exit 0
bundle exec ruby examples/gentle_deadline.rb
a real captured run
THE GENTLE DEADLINE (essential courses always; garnish as time allows)
a leisurely evening (budget 500ms):
served 6 courses in 256ms - status: completed
everything made it, garnish and all
a hurried lunch (budget 160ms):
served 3 courses in 129ms - status: completed
declined with regrets: garnish: related links; garnish: pull quotes; dessert: summary haiku
(the meal was still served - nobody saw an error page)
the design is one question asked politely: before an OPTIONAL
task starts, is there comfortably time for it AND the essentials
still owed? if not, it declines BY NAME and the plan flows on.
compare the violent alternative - a global timeout that kills
the render step because the pull quotes ran long, serving the
user an error instead of a plainer dinner. deadlines are not
the enemy of graciousness; treating every task as equally
essential is. mark the garnish as garnish, and lateness becomes
a menu decision instead of an outage.
source
# frozen_string_literal: true # The Gentle Deadline: most deadline code is violent - a timeout # fires, everything dies, the user gets an error page at 30.0 # seconds that could have been a good-enough answer at 29. This plan # knows which of its tasks are ESSENTIAL and which are garnish, and # when the time budget runs low it starts declining the garnish - # politely, by name, with the meal still served on time. # # bundle exec ruby examples/gentle_deadline.rb # # Runs offline; the same dinner is cooked twice, hurried once. require class="s">"bundler/setup" require class="s">"agentic" Agentic.logger.level = class="y">:fatal # Course list: essential tasks make the meal; optional ones make it lovely COURSES = [ {name: class="s">"stock: fetch data", essential: true, cost: 0.04}, {name: class="s">"main: analyze", essential: true, cost: 0.06}, {name: class="s">"garnish: related links", essential: false, cost: 0.05}, {name: class="s">"garnish: pull quotes", essential: false, cost: 0.05}, {name: class="s">"dessert: summary haiku", essential: false, cost: 0.03}, {name: class="s">"serve: render answer", essential: true, cost: 0.02} ].freeze def cook(budget_seconds) deadline = Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) + budget_seconds orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 1) declined = [] previous = nil COURSES.each do |course| task = Agentic:class="y">:Task.new(description: course[class="y">:name], agent_spec: {class="s">"name" => course[class="y">:name], class="s">"instructions" => class="s">"cook"}) orchestrator.add_task(task, previous ? [previous] : [], agent: ->(_t) { remaining = deadline - Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) # The gentle part: an optional course checks whether there is # comfortably time for it AND the essentials still to come, and # bows out by name instead of being murdered mid-simmer essentials_owed = COURSES.drop(COURSES.index(course) + 1).select { |c| c[class="y">:essential] }.sum { |c| c[class="y">:cost] } if !course[class="y">:essential] && remaining < course[class="y">:cost] + essentials_owed + 0.01 declined << course[class="y">:name] next class="y">:declined_with_regrets end sleep(course[class="y">:cost]) class="s">"#{course[class="y">:name]} ready" }) previous = task end started = Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) result = orchestrator.execute_plan [result, Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) - started, declined] end puts class="s">"THE GENTLE DEADLINE (essential courses always; garnish as time allows)" puts [[class="s">"a leisurely evening", 0.5], [class="s">"a hurried lunch", 0.16]].each do |occasion, budget| result, elapsed, declined = cook(budget) served = result.results.values.count { |r| r.successful? && r.output != class="y">:declined_with_regrets } puts class="s">" #{occasion} (budget #{(budget * 1000).round}ms):" puts format(class="s">" served %d courses in %dms - status: %s", served, (elapsed * 1000).round, result.status) if declined.any? puts class="s">" declined with regrets: #{declined.join("; class="s">")}" puts class="s">" (the meal was still served - nobody saw an error page)" else puts class="s">" everything made it, garnish and all" end puts end puts class="s">" the design is one question asked politely: before an OPTIONAL" puts class="s">" task starts, is there comfortably time for it AND the essentials" puts class="s">" still owed? if not, it declines BY NAME and the plan flows on." puts class="s">" compare the violent alternative - a global timeout that kills" puts class="s">" the render step because the pull quotes ran long, serving the" puts class="s">" user an error instead of a plainer dinner. deadlines are not" puts class="s">" the enemy of graciousness; treating every task as equally" puts class="s">" essential is. mark the garnish as garnish, and lateness becomes" puts class="s">" a menu decision instead of an outage."