agentic examples

The Namespace Cartographer

The Namespace Cartographer: maps a gem's constant tree and audits every file against the constant Zeitwerk expects it to define. One orchestrator task per file; Prism reads the actual definitions.

Plans & Graphs Round 2 Xavier Noria exit 0

source on github

bundle exec ruby examples/namespace_cartographer.rb

a real captured run

NAMESPACE MAP of /opt/hostedtoolcache/Ruby/3.3.11/x64/lib/ruby/gems/3.3.0/bundler/gems/agentic-0fe3f91c1ef9/lib
(68 files surveyed, completed in 482ms)

  agentic                                          1 file(s)
  agentic/adaptation_engine                        1 file(s)
  agentic/agent                                    1 file(s)
  agentic/agent_assembly_engine                    1 file(s)
  agentic/agent_capability_registry                1 file(s)
  agentic/agent_config                             1 file(s)
  agentic/agent_specification                      1 file(s)
  agentic/callable_agent                           1 file(s)
  agentic/capabilities                             3 file(s)
  agentic/capability_provider                      1 file(s)
  agentic/capability_specification                 1 file(s)
  agentic/capability_validator                     1 file(s)
  agentic/cli                                      5 file(s)
  agentic/default_agent_provider                   1 file(s)
  agentic/errors                                   1 file(s)
  agentic/execution_journal                        1 file(s)
  agentic/execution_plan                           1 file(s)
  agentic/execution_result                         1 file(s)
  agentic/expected_answer_format                   1 file(s)
  agentic/extension                                4 file(s)
  agentic/factory_methods                          1 file(s)
  agentic/generation_stats                         1 file(s)
  agentic/learning                                 5 file(s)
  agentic/llm_assisted_composition_strategy        1 file(s)
  agentic/llm_client                               1 file(s)
  agentic/llm_config                               1 file(s)
  agentic/llm_response                             1 file(s)
  agentic/logger                                   1 file(s)
  agentic/named_outputs                            1 file(s)
  agentic/observable                               1 file(s)
  agentic/persistent_agent_store                   1 file(s)
  agentic/plan_execution_result                    1 file(s)
  agentic/plan_orchestrator                        1 file(s)
  agentic/plan_orchestrator_config                 1 file(s)
  agentic/rate_limit                               1 file(s)
  agentic/relation_rules                           1 file(s)
  agentic/retry_config                             1 file(s)
  agentic/retry_handler                            1 file(s)
  agentic/structured_outputs                       1 file(s)
  agentic/suggestions                              1 file(s)
  agentic/task                                     1 file(s)
  agentic/task_definition                          1 file(s)
  agentic/task_execution_result                    1 file(s)
  agentic/task_failure                             1 file(s)
  agentic/task_output_schemas                      1 file(s)
  agentic/task_planner                             1 file(s)
  agentic/task_result                              1 file(s)
  agentic/ui                                       1 file(s)
  agentic/verification                             6 file(s)
  agentic/version                                  1 file(s)

Every file defines the constant its path promises. The map IS the territory.

source

# frozen_string_literal: true

# The Namespace Cartographer: maps a gem's constant tree and audits
# every file against the constant Zeitwerk expects it to define.
# One orchestrator task per file; Prism reads the actual definitions.
#
#   bundle exec ruby examples/namespace_cartographer.rb [lib_dir]
#
# Defaults to surveying this gem. A conforming codebase produces a map
# with no annotations; every deviation is listed with what was expected
# and what was found.

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
INFLECTIONS = {class="s">"cli" => class="s">"CLI", class="s">"ui" => class="s">"UI"}.freeze

def camelize(segment)
  INFLECTIONS.fetch(segment) { segment.split(class="s">"_").map(&class="y">:capitalize).join }
end

# The constant Zeitwerk expects lib/foo/bar_baz.rb to define.
# Zeitwerk::GemInflector special-cases the gem's version.rb: it expects
# Foo::VERSION, not Foo::Version - a lesson this cartographer learned
# by first drawing the deviation on its own map.
def expected_constant(relative_path)
  segments = relative_path.delete_suffix(class="s">".rb").split(class="s">"/")
  return class="s">"#{camelize(segments.first)}:class="y">:VERSION" if segments.length == 2 && segments.last == class="s">"version"

  segments.map { |seg| camelize(seg) }.join(class="s">"::")
end

# Collects every module/class defined in a parse tree, as full paths
def collect_definitions(node, namespace, found)
  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">"::")
    found << full
    node.child_nodes.each { |child| collect_definitions(child, full, found) }
  when Prism:class="y">:ConstantWriteNode
    found << [namespace, node.name.to_s].reject(&class="y">:empty?).join(class="s">"::")
  else
    node.child_nodes.each { |child| collect_definitions(child, namespace, found) }
  end
end

spec = Agentic:class="y">:CapabilitySpecification.new(
  name: class="s">"survey_file",
  description: class="s">"Chart the constants a Ruby file defines",
  version: class="s">"1.0.0",
  inputs: {path: {type: class="s">"string", required: true}},
  outputs: {defined: {type: class="s">"array", required: true}}
)
Agentic.register_capability(spec, Agentic:class="y">:CapabilityProvider.new(
  capability: spec,
  implementation: ->(inputs) {
    found = []
    collect_definitions(Prism.parse_file(inputs[class="y">:path]).value, class="s">"", found)
    {defined: found}
  }
))

surveyor = Agentic:class="y">:Agent.build { |a| a.name = class="s">"Cartographer" }
surveyor.add_capability(class="s">"survey_file")

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
orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 8)
tasks = files.to_h do |path|
  task = Agentic:class="y">:Task.new(
    description: File.basename(path),
    agent_spec: {class="s">"name" => class="s">"Cartographer", class="s">"instructions" => class="s">"Survey the file"},
    payload: path
  )
  orchestrator.add_task(task, agent: ->(t) {
    surveyor.execute_capability(class="s">"survey_file", {path: t.payload})[class="y">:defined]
  })
  [path, task]
end
result = orchestrator.execute_plan
charts = tasks.transform_values { |task| result.results[task.id].output }

# Compare the map against the territory
deviations = []
tree = Hash.new(0)
files.each do |path|
  relative = path.delete_prefix(class="s">"#{LIB}/")
  expected = expected_constant(relative)
  defined = charts.fetch(path, [])

  tree[relative.split(class="s">"/").first(2).join(class="s">"/").delete_suffix(class="s">".rb")] += 1
  unless defined.include?(expected)
    deviations << {file: relative, expected: expected, found: defined.first(3)}
  end
end

puts class="s">"NAMESPACE MAP of #{LIB}"
puts class="s">"(#{files.size} files surveyed, #{result.status} in #{(result.execution_time * 1000).round}ms)"
puts
tree.sort.each { |region, count| puts format(class="s">"  %-46s %3d file(s)", region, count) }
puts
if deviations.empty?
  puts class="s">"Every file defines the constant its path promises. The map IS the territory."
else
  puts class="s">"DEVIATIONS (#{deviations.size}):"
  deviations.each do |d|
    puts class="s">"  #{d[class="y">:file]}"
    puts class="s">"    expected #{d[class="y">:expected]}, defines #{d[class="y">:found].join(", class="s">")}"
  end
end