The Etude Machine
The Etude Machine: deliberate practice for plan-builders. An etude is a small broken plan, a hint, and a hidden test - you fix the plan until the test passes, exactly like an exercism exercise. But the machine holds ITSELF to the practice-room standard before any student arrives: every etude's broken form must FAIL the hidden test (an exercise that passes before you touch it teaches nothing), every etude must carry a model solution that PASSES (unsolvable exercises are …
Testing & Verification
Round 18
Katrina Owen
exit 0
bundle exec ruby examples/etude_machine.rb
a real captured run
THE ETUDE MACHINE (scales before songs)
1. The Missing Thread (concepts: dependencies)
hint: greet reads previous_output... from whom?
broken form fails the hidden test: yes (as an exercise must)
model solution passes: yes (solvable, not hazing)
2. The Swapped Hats (concepts: dependencies, agents)
hint: each agent is wearing the other's job description
broken form fails the hidden test: yes (as an exercise must)
model solution passes: yes (solvable, not hazing)
3. The Stubborn Courier (concepts: dependencies, agents, retry_policy)
hint: the courier fails TRANSIENTLY; the plan gives up permanently. one policy line.
broken form fails the hidden test: yes (as an exercise must)
model solution passes: yes (solvable, not hazing)
curriculum: 1 -> 2 -> 3 concepts per etude - each adds exactly one.
the machine holds itself to the practice-room standard: every
broken form FAILS (an exercise that passes before you touch it
teaches nothing but false confidence), every etude ships a model
solution that PASSES (unsolvable exercises aren't rigor, they're
hazing), and difficulty climbs one concept at a time - thread a
dependency, then assign the right agent, then write a retry
policy. that ordering is the actual pedagogy: practice is only
deliberate when the next rung is exactly one reach away, and
the feedback - a hidden test with an exit code - arrives in
seconds, not in code review three weeks later. fluency in a
framework is built the same way as fluency in anything: small
pieces, immediate feedback, rising difficulty, no free passes.
source
# frozen_string_literal: true # The Etude Machine: deliberate practice for plan-builders. An etude # is a small broken plan, a hint, and a hidden test - you fix the # plan until the test passes, exactly like an exercism exercise. But # the machine holds ITSELF to the practice-room standard before any # student arrives: every etude's broken form must FAIL the hidden # test (an exercise that passes before you touch it teaches # nothing), every etude must carry a model solution that PASSES # (unsolvable exercises are hazing), and difficulty must climb one # concept at a time - scales before songs. # # bundle exec ruby examples/etude_machine.rb # # Runs offline; exits 1 unless every etude fails broken, passes # solved, and the curriculum climbs monotonically. require class="s">"bundler/setup" require class="s">"agentic" Agentic.logger.level = class="y">:fatal # Each etude: a builder that assembles the plan either :broken or # :solved, a hidden test over the result, a hint, and the concepts # the solution exercises (the difficulty score is their count) ETUDES = [ { title: class="s">"1. The Missing Thread", hint: class="s">"greet reads previous_output... from whom?", concepts: [class="y">:dependencies], test: ->(result, tasks) { result.task_result(tasks[class="y">:greet].id)&.output == class="s">"hello, world" }, build: ->(mode) { orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 2) name = Agentic:class="y">:Task.new(description: class="s">"fetch name", agent_spec: {class="s">"name" => class="s">"n", class="s">"instructions" => class="s">"w"}) greet = Agentic:class="y">:Task.new(description: class="s">"greet", agent_spec: {class="s">"name" => class="s">"g", class="s">"instructions" => class="s">"w"}) orchestrator.add_task(name, agent: ->(_t) { class="s">"world" }) deps = (mode == class="y">:solved) ? [name] : [] # the thread, missing orchestrator.add_task(greet, deps, agent: ->(t) { class="s">"hello, #{t.previous_output}" }) [orchestrator, {name: name, greet: greet}] } }, { title: class="s">"2. The Swapped Hats", hint: class="s">"each agent is wearing the other's job description", concepts: [class="y">:dependencies, class="y">:agents], test: ->(result, tasks) { result.task_result(tasks[class="y">:publish].id)&.output == class="s">"PUBLISHED: draft of 3 words" }, build: ->(mode) { orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 2) write = Agentic:class="y">:Task.new(description: class="s">"write", agent_spec: {class="s">"name" => class="s">"w", class="s">"instructions" => class="s">"w"}) publish = Agentic:class="y">:Task.new(description: class="s">"publish", agent_spec: {class="s">"name" => class="s">"p", class="s">"instructions" => class="s">"w"}) writer = ->(_t) { class="s">"draft of 3 words" } publisher = ->(t) { class="s">"PUBLISHED: #{t.previous_output}" } first, second = (mode == class="y">:solved) ? [writer, publisher] : [publisher, writer] orchestrator.add_task(write, agent: first) orchestrator.add_task(publish, [write], agent: second) [orchestrator, {write: write, publish: publish}] } }, { title: class="s">"3. The Stubborn Courier", hint: class="s">"the courier fails TRANSIENTLY; the plan gives up permanently. one policy line.", concepts: [class="y">:dependencies, class="y">:agents, class="y">:retry_policy], test: ->(result, tasks) { result.task_result(tasks[class="y">:deliver].id)&.output == class="s">"delivered on attempt 2" }, build: ->(mode) { policy = (mode == class="y">:solved) ? {max_retries: 2, backoff_base: 0.005, retryable_errors: [Agentic:class="y">:Errors:class="y">:LlmRateLimitError]} : {max_retries: 0, retryable_errors: []} orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 1, retry_policy: policy) deliver = Agentic:class="y">:Task.new(description: class="s">"deliver", agent_spec: {class="s">"name" => class="s">"d", class="s">"instructions" => class="s">"w"}) attempts = 0 orchestrator.add_task(deliver, agent: ->(_t) { attempts += 1 raise Agentic:class="y">:Errors:class="y">:LlmRateLimitError, class="s">"dog at the gate" if attempts == 1 class="s">"delivered on attempt #{attempts}" }) [orchestrator, {deliver: deliver}] } } ].freeze puts class="s">"THE ETUDE MACHINE (scales before songs)" puts failures = [] difficulties = [] ETUDES.each do |etude| broken_result, broken_tasks = etude[class="y">:build].call(class="y">:broken).then { |o, t| [o.execute_plan, t] } solved_result, solved_tasks = etude[class="y">:build].call(class="y">:solved).then { |o, t| [o.execute_plan, t] } fails_broken = !etude[class="y">:test].call(broken_result, broken_tasks) passes_solved = etude[class="y">:test].call(solved_result, solved_tasks) difficulties << etude[class="y">:concepts].size puts class="s">" #{etude[class="y">:title]} (concepts: #{etude[class="y">:concepts].join(", class="s">")})" puts class="s">" hint: #{etude[class="y">:hint]}" puts class="s">" broken form fails the hidden test: #{fails_broken ? "yes (as an exercise must)class="s">" : "NO - free pass, worthlessclass="s">"}" puts class="s">" model solution passes: #{passes_solved ? "yes (solvable, not hazing)class="s">" : "NO - unsolvableclass="s">"}" puts failures << class="s">"#{etude[class="y">:title]} gives a free pass" unless fails_broken failures << class="s">"#{etude[class="y">:title]} is unsolvable" unless passes_solved end failures << class="s">"curriculum difficulty not monotonic: #{difficulties.inspect}" unless difficulties.each_cons(2).all? { |a, b| a <= b } puts class="s">" curriculum: #{difficulties.join(" -> class="s">")} concepts per etude - each adds exactly one." puts puts class="s">" the machine holds itself to the practice-room standard: every" puts class="s">" broken form FAILS (an exercise that passes before you touch it" puts class="s">" teaches nothing but false confidence), every etude ships a model" puts class="s">" solution that PASSES (unsolvable exercises aren't rigor, they're" puts class="s">" hazing), and difficulty climbs one concept at a time - thread a" puts class="s">" dependency, then assign the right agent, then write a retry" puts class="s">" policy. that ordering is the actual pedagogy: practice is only" puts class="s">" deliberate when the next rung is exactly one reach away, and" puts class="s">" the feedback - a hidden test with an exit code - arrives in" puts class="s">" seconds, not in code review three weeks later. fluency in a" puts class="s">" framework is built the same way as fluency in anything: small" puts class="s">" pieces, immediate feedback, rising difficulty, no free passes." exit(failures.empty? ? 0 : 1)