agentic examples

Rule Shapes

Rule Shapes: the same policy - "express shipments need a customs code" - written three ways: a lambda, a structured check, and a relation. Then four consumers try to use each shape: the validator, the message deriver, the fixture generator, and the schema export. Representation isn't style; it's a decision about who else gets to understand you.

Data & Pipelines Round 10 Sandi Metz exit 0

source on github

bundle exec ruby examples/rule_shapes.rb

a real captured run

RULE SHAPES: one policy, three representations, four consumers

  shape                  enforced   explains   generatable  projects
  lambda                 yes        no         no           no
  structured check       yes        yes        no           no
  relation               yes        yes        yes          yes

  all three shapes enforce - if enforcement were the whole job,
  they'd be interchangeable and you'd pick by taste. but the
  lambda answers ONE message (call) so it has ONE consumer; the
  structured check adds fields: and message:, so violations can
  explain themselves; and the relation makes the predicate itself
  data, so tools that never RUN it - the generator, the schema
  export, round 10's diff - can still read it. choose the
  representation by counting who must understand it: code keeps
  secrets, data makes friends. save lambdas for policies that
  are genuinely secrets.

source

# frozen_string_literal: true

# Rule Shapes: the same policy - "express shipments need a customs
# code" - written three ways: a lambda, a structured check, and a
# relation. Then four consumers try to use each shape: the validator,
# the message deriver, the fixture generator, and the schema export.
# Representation isn't style; it's a decision about who else gets to
# understand you.
#
#   bundle exec ruby examples/rule_shapes.rb
#
# Runs offline; the table is the argument.

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

INPUTS = {
  express: {type: class="s">"boolean"},
  customs_code: {type: class="s">"string"}
}.freeze

SHAPES = {
  class="s">"lambda" => {
    class="s">"express needs customs" => ->(i) { !i[class="y">:express] || !i[class="y">:customs_code].nil? }
  },
  class="s">"structured check" => {
    customs: {message: class="s">"express shipments need a customs code",
              fields: [class="y">:express, class="y">:customs_code],
              check: ->(i) { !i[class="y">:express] || !i[class="y">:customs_code].nil? }}
  },
  class="s">"relation" => {
    customs: {relation: class="y">:requires, fields: [class="y">:express, class="y">:customs_code]}
  }
}.freeze

def spec_with(rules)
  Agentic:class="y">:CapabilitySpecification.new(
    name: class="s">"ship", description: class="s">"Ship it", version: class="s">"1.0.0", inputs: INPUTS, rules: rules
  )
end

# Consumer 1: can the validator enforce it?
def enforces?(spec)
  Agentic:class="y">:CapabilityValidator.new(spec).validate_inputs!(express: true)
  false
rescue Agentic:class="y">:Errors:class="y">:ValidationError
  true
end

# Consumer 2: does a violation point at its fields, with a real message?
def explains?(spec)
  Agentic:class="y">:CapabilityValidator.new(spec).validate_inputs!(express: true)
  false
rescue Agentic:class="y">:Errors:class="y">:ValidationError => e
  violation = e.rule_violations.first
  violation[class="y">:fields].any? && !violation[class="y">:message].match?(/\A(rule_)?\d*\z/)
end

# Consumer 3: can a generator SATISFY it without running it blind?
# (Only a declared predicate can be satisfied constructively)
def generatable?(rules)
  rules.values.all? { |d| !d.respond_to?(class="y">:call) && d[class="y">:relation] }
end

# Consumer 4: does it reach the JSON Schema export as a real keyword?
def projects?(spec)
  schema = spec.to_json_schema
  !(schema[class="s">"dependencies"] || schema[class="s">"allOf"]).nil?
end

puts class="s">"RULE SHAPES: one policy, three representations, four consumers"
puts
puts format(class="s">"  %-22s %-10s %-10s %-12s %s", class="s">"shape", class="s">"enforced", class="s">"explains", class="s">"generatable", class="s">"projects")
SHAPES.each do |name, rules|
  spec = spec_with(rules)
  puts format(class="s">"  %-22s %-10s %-10s %-12s %s",
    name,
    enforces?(spec) ? class="s">"yes" : class="s">"NO",
    explains?(spec) ? class="s">"yes" : class="s">"no",
    generatable?(rules) ? class="s">"yes" : class="s">"no",
    projects?(spec) ? class="s">"yes" : class="s">"no")
end

puts
puts class="s">"  all three shapes enforce - if enforcement were the whole job,"
puts class="s">"  they'd be interchangeable and you'd pick by taste. but the"
puts class="s">"  lambda answers ONE message (call) so it has ONE consumer; the"
puts class="s">"  structured check adds fields: and message:, so violations can"
puts class="s">"  explain themselves; and the relation makes the predicate itself"
puts class="s">"  data, so tools that never RUN it - the generator, the schema"
puts class="s">"  export, round 10's diff - can still read it. choose the"
puts class="s">"  representation by counting who must understand it: code keeps"
puts class="s">"  secrets, data makes friends. save lambdas for policies that"
puts class="s">"  are genuinely secrets."