The Onboarding Trail
The Onboarding Trail: a codebase is a place people live, and new teammates don't need a map of every pipe - they need a TOUR: which room to enter first, and why each room makes sense given the ones you've seen. This computes the tour from the code itself: scan who mentions whom, then order the rooms so no stop assumes a concept you haven't met yet.
Developer Experience
Round 12
Sarah Mei
exit 0
bundle exec ruby examples/onboarding_trail.rb
a real captured run
THE ONBOARDING TRAIL (computed from who-mentions-whom)
day one, in order - no room assumes one you haven't seen:
1. task_failure 98 lines how this house talks about things going wrong (failure as data)
2. task_result 48 lines the envelope every outcome arrives in
(mentions task_failure)
3. relation_rules 109 lines predicates as data - rules tools can read
4. rate_limit 204 lines the shared front door: ceilings, windows, resize
5. task 226 lines the unit of work: lifecycle, payloads, needs
(mentions task_failure, task_result)
6. capability_specification 227 lines contracts: declared inputs, outputs, rules
(mentions relation_rules)
7. capability_validator 176 lines the barricade that enforces the contracts
(mentions relation_rules, capability_specification)
8. plan_orchestrator 748 lines the living room where everything meets: scheduling, hooks, the graph
(mentions task_failure, task)
9. execution_journal 255 lines the house's memory: fsynced, replayable, per-shard
(mentions task, plan_orchestrator)
why a trail instead of a map: a map answers "where is" and
nobody's first question is where - it's "what should I read
FIRST so the rest makes sense?" the ordering came from the
code (fewest unmet concepts next), and the one-line room notes
came from a human, which is the correct split: structure is
derivable, PURPOSE isn't. notice the trail starts with failure -
this house talks about failure before it talks about work, and
a new teammate who learns that on day one has learned the
house's values, not just its layout. codebases are places
people live; give the new roommate a tour, not a blueprint.
source
# frozen_string_literal: true # The Onboarding Trail: a codebase is a place people live, and new # teammates don't need a map of every pipe - they need a TOUR: which # room to enter first, and why each room makes sense given the ones # you've seen. This computes the tour from the code itself: scan who # mentions whom, then order the rooms so no stop assumes a concept # you haven't met yet. # # bundle exec ruby examples/onboarding_trail.rb # # Runs offline; the trail is derived, not curated (mostly). require class="s">"bundler/setup" require class="s">"agentic" # These examples read the agentic SOURCE - resolve the installed gem's own directory AGENTIC_SRC = Gem:class="y">:Specification.find_by_name(class="s">"agentic").gem_dir LIB = File.join(AGENTIC_SRC, class="s">"lib/agentic") # What each room is FOR - the one sentence a tour guide adds that a # dependency graph can't ROOM_NOTES = { class="s">"task_failure" => class="s">"how this house talks about things going wrong (failure as data)", class="s">"task_result" => class="s">"the envelope every outcome arrives in", class="s">"task" => class="s">"the unit of work: lifecycle, payloads, needs", class="s">"rate_limit" => class="s">"the shared front door: ceilings, windows, resize", class="s">"execution_journal" => class="s">"the house's memory: fsynced, replayable, per-shard", class="s">"relation_rules" => class="s">"predicates as data - rules tools can read", class="s">"capability_specification" => class="s">"contracts: declared inputs, outputs, rules", class="s">"capability_validator" => class="s">"the barricade that enforces the contracts", class="s">"plan_orchestrator" => class="s">"the living room where everything meets: scheduling, hooks, the graph" }.freeze # Who mentions whom, from the source itself files = ROOM_NOTES.keys.to_h do |name| source = File.read(File.join(LIB, class="s">"#{name}.rb"), encoding: class="s">"UTF-8") constants = source.scan(/\b(?class="y">:Agentic::)?([A-Z][A-Za-z]+)\b/).flatten.uniq mentioned = ROOM_NOTES.keys.select { |other| other != name && constants.include?(other.split(class="s">"_").map(&class="y">:capitalize).join) } [name, {mentions: mentioned, lines: source.lines.size}] end # The trail: repeatedly visit the room with the fewest unmet mentions trail = [] until trail.size == files.size next_room = files.keys.reject { |f| trail.include?(f) } .min_by { |f| [(files[f][class="y">:mentions] - trail).size, files[f][class="y">:lines]] } trail << next_room end puts class="s">"THE ONBOARDING TRAIL (computed from who-mentions-whom)" puts puts class="s">" day one, in order - no room assumes one you haven't seen:" puts trail.each_with_index do |room, index| unmet = files[room][class="y">:mentions] - trail[0..index] puts format(class="s">" %d. %-26s %4d lines %s", index + 1, room, files[room][class="y">:lines], ROOM_NOTES[room]) puts format(class="s">" %s", class="s">"(mentions #{files[room][class="y">:mentions].join(", class="s">")})") if files[room][class="y">:mentions].any? puts class="s">" WARNING: tour visits this before #{unmet.join(", class="s">")}" if unmet.any? end puts puts class="s">" why a trail instead of a map: a map answers \"where is\class="s">" and" puts class="s">" nobody's first question is where - it's \"what should I read" puts class="s">" FIRST so the rest makes sense?\" the ordering came from the" puts class="s">" code (fewest unmet concepts next), and the one-line room notes" puts class="s">" came from a human, which is the correct split: structure is" puts class="s">" derivable, PURPOSE isn't. notice the trail starts with failure -" puts class="s">" this house talks about failure before it talks about work, and" puts class="s">" a new teammate who learns that on day one has learned the" puts class="s">" house's values, not just its layout. codebases are places" puts class="s">" people live; give the new roommate a tour, not a blueprint."