agentic examples

The Coupling Cartographer

The Coupling Cartographer: which files lean on which? Every file is surveyed for the constants it DEFINES and the constants it REFERENCES; a fan-in task joins the two into a dependency graph and reports the load-bearing walls and the heaviest leaners.

Data & Pipelines Round 4 Xavier Noria exit 0

source on github

bundle exec ruby examples/coupling_cartographer.rb

a real captured run

COUPLING ATLAS of /opt/hostedtoolcache/Ruby/3.3.11/x64/lib/ruby/gems/3.3.0/bundler/gems/agentic-0fe3f91c1ef9/lib (68 files)

load-bearing walls (most depended-upon):
   8 files lean on  agentic/llm_config.rb
   7 files lean on  agentic/errors.rb
   5 files lean on  agentic/verification/verification_strategy.rb
   5 files lean on  agentic/agent_capability_registry.rb
   5 files lean on  agentic/cli/capabilities.rb
   5 files lean on  agentic/cli/agent.rb

heaviest leaners (most dependencies out):
  agentic/cli.rb                           leans on 15 files
  agentic.rb                               leans on 12 files
  agentic/task_planner.rb                  leans on  6 files
  agentic/task.rb                          leans on  6 files
  agentic/plan_orchestrator.rb             leans on  6 files
  agentic/capabilities/examples.rb         leans on  6 files

mutual dependencies (each file references the other):
  agentic.rb <-> agentic/llm_client.rb

source

# frozen_string_literal: true

# The Coupling Cartographer: which files lean on which? Every file is
# surveyed for the constants it DEFINES and the constants it REFERENCES;
# a fan-in task joins the two into a dependency graph and reports the
# load-bearing walls and the heaviest leaners.
#
#   bundle exec ruby examples/coupling_cartographer.rb [lib_dir]
#
# Runs offline; Prism supplies both sides of every edge.

require class="s">"bundler/setup"
require class="s">"agentic"
require class="s">"prism"

LIB = ARGV.first ? File.expand_path(ARGV.first) : File.join(Gem:class="y">:Specification.find_by_name(class="s">"agentic").gem_dir, class="s">"lib") # the installed gem, wherever bundler put it

# Collects constants defined and referenced in one parse tree
def survey_constants(node, namespace, defined, referenced)
  return unless node

  case node
  when Prism:class="y">:ModuleNode, Prism:class="y">:ClassNode
    name = node.constant_path.slice
    full = [namespace, name].reject(&class="y">:empty?).join(class="s">"::")
    defined << full
    node.child_nodes.each { |child| survey_constants(child, full, defined, referenced) }
    return
  when Prism:class="y">:ConstantReadNode
    referenced << node.name.to_s
  when Prism:class="y">:ConstantPathNode
    referenced << node.slice
  end

  node.child_nodes.each { |child| survey_constants(child, namespace, defined, referenced) }
end

orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 8)
files = Dir[File.join(LIB, class="s">"**", class="s">"*.rb")].sort
if files.empty?
  puts class="s">"  no ruby files under #{LIB} - an empty survey proves nothing"
  exit 1
end

surveys = files.map do |path|
  task = Agentic:class="y">:Task.new(
    description: path.delete_prefix(class="s">"#{LIB}/"),
    agent_spec: {class="s">"name" => class="s">"Surveyor", class="s">"instructions" => class="s">"chart the constants"},
    payload: path
  )
  orchestrator.add_task(task, agent: ->(t) {
    defined = []
    referenced = []
    survey_constants(Prism.parse_file(t.payload).value, class="s">"", defined, referenced)
    {defined: defined, referenced: referenced.uniq - defined}
  })
  task
end

atlas = Agentic:class="y">:Task.new(
  description: class="s">"the atlas",
  agent_spec: {class="s">"name" => class="s">"Cartographer", class="s">"instructions" => class="s">"join the maps"}
)
orchestrator.add_task(atlas, surveys, agent: ->(t) {
  charts = surveys.to_h { |s| [s.description, t.output_of(s)] }

  # Who owns each constant (by trailing segment, since references are
  # often relative: LlmClient rather than Agentic::LlmClient)
  owners = {}
  charts.each do |file, chart|
    chart[class="y">:defined].each { |const| owners[const.split(class="s">"::").last] = file }
  end

  edges = Hash.new { |h, k| h[k] = [] }
  charts.each do |file, chart|
    chart[class="y">:referenced].each do |ref|
      owner = owners[ref.split(class="s">"::").last]
      edges[file] << owner if owner && owner != file
    end
  end
  # Copy without the default proc: a Hash.new {} that leaks to readers
  # invents keys on every miss - including during iteration
  edges = edges.transform_values(&class="y">:uniq)

  inbound = Hash.new(0)
  edges.each_value { |targets| targets.each { |target| inbound[target] += 1 } }

  {edges: edges, inbound: inbound}
})

result = orchestrator.execute_plan
atlas_data = result.results[atlas.id].output

puts class="s">"COUPLING ATLAS of #{LIB} (#{files.size} files)"
puts
puts class="s">"load-bearing walls (most depended-upon):"
atlas_data[class="y">:inbound].sort_by { |_, count| -count }.first(6).each do |file, count|
  puts format(class="s">"  %2d files lean on  %s", count, file)
end
puts
puts class="s">"heaviest leaners (most dependencies out):"
atlas_data[class="y">:edges].sort_by { |_, targets| -targets.size }.first(6).each do |file, targets|
  puts format(class="s">"  %-40s leans on %2d files", file, targets.size)
end

mutual = atlas_data[class="y">:edges].flat_map { |file, targets|
  targets.filter_map { |target| [file, target].sort if atlas_data[class="y">:edges][target]&.include?(file) }
}.uniq
puts
if mutual.empty?
  puts class="s">"no mutual dependencies - every edge points one way. rare, and good."
else
  puts class="s">"mutual dependencies (each file references the other):"
  mutual.each { |a, b| puts class="s">"  #{a} <-> #{b}" }
end