agentic examples

Money Discipline

Money Discipline: every money bug in production is the same three bugs - floats for currency, arithmetic before validation, and rounding decided at the last minute by whoever's line of code got there first. This runs an invoicing plan twice: once the way demos do it (floats), once the way ledgers demand (integer cents, a Money value object, rounding policy declared at the boundary) - and lets a penny audit judge them both.

Observability & Ops Round 13 Noel Rappin exit 0

source on github

bundle exec ruby examples/money_discipline.rb

a real captured run

MONEY DISCIPLINE (same invoice, two arithmetics)

               float version                ledger version
  subtotal     230.19999999999999           $230.20
  tax          18.99150000000000            $18.99
  total        249.19149999999999           $249.19

  the ledger version signed its contract: integer cents, all
  non-negative, and the adds_up rule verified to the penny.

  now read the float column like an accountant: the subtotal ends
  in ...999999, because 0.1 x 3 is not 0.3 in binary - IEEE 754
  is already paying out interest. today it rounds to the
  right penny (24919); at some other quantity or rate it won't,
  and the discrepancy will surface in a reconciliation report
  eleven months from now, assigned to whoever touched the code
  last. the discipline is three sentences: money is integer
  cents (a TYPE the contract can enforce - "integer" isn't
  pedantry, it's a tripwire); rounding is a NAMED policy applied
  at declared points (banker's, at multiplication), not an
  accident of printf; and the books must balance BY RULE
  (adds_up), not by hope. take my money - but count it in cents.

source

# frozen_string_literal: true

# Money Discipline: every money bug in production is the same three
# bugs - floats for currency, arithmetic before validation, and
# rounding decided at the last minute by whoever's line of code got
# there first. This runs an invoicing plan twice: once the way demos
# do it (floats), once the way ledgers demand (integer cents, a Money
# value object, rounding policy declared at the boundary) - and lets
# a penny audit judge them both.
#
#   bundle exec ruby examples/money_discipline.rb
#
# Runs offline; the discrepancy is real IEEE 754, not contrivance.

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

Agentic.logger.level = class="y">:fatal

LINE_ITEMS = [
  {desc: class="s">"api calls", unit_price: 0.1, qty: 3},
  {desc: class="s">"storage", unit_price: 29.99, qty: 3},
  {desc: class="s">"seats", unit_price: 19.99, qty: 7}
].freeze
TAX_RATE = 0.0825

# --- the demo version: floats all the way down ----------------------------------
def float_invoice(items)
  subtotal = items.sum { |i| i[class="y">:unit_price] * i[class="y">:qty] }
  tax = subtotal * TAX_RATE
  {subtotal: subtotal, tax: tax, total: subtotal + tax}
end

# --- the ledger version: integer cents + one rounding policy --------------------
# Money is a value object: cents inside, arithmetic closed, rounding
# NAMED (banker's here) and applied exactly where policy says
Money = Struct.new(class="y">:cents) do
  def +(other) = Money.new(cents + other.cents)

  def *(other) = Money.new((cents * other).round(half: class="y">:even))

  def to_s = format(class="s">"$%d.%02d", cents / 100, cents % 100)
end

def cents(dollars) = Money.new((dollars * 100).round)

CONTRACT = Agentic:class="y">:CapabilitySpecification.new(
  name: class="s">"invoice", description: class="s">"Price an invoice", version: class="s">"1.0.0",
  inputs: {items: {type: class="s">"array", required: true, non_empty: true}},
  outputs: {
    subtotal_cents: {type: class="s">"integer", required: true, min: 0},
    tax_cents: {type: class="s">"integer", required: true, min: 0},
    total_cents: {type: class="s">"integer", required: true, min: 0}
  },
  rules: {
    adds_up: {message: class="s">"total must equal subtotal + tax, to the penny",
              fields: [class="y">:subtotal_cents, class="y">:tax_cents, class="y">:total_cents],
              check: ->(o) { o[class="y">:total_cents] == o[class="y">:subtotal_cents] + o[class="y">:tax_cents] }}
  }
)

def ledger_invoice(items)
  subtotal = items.map { |i| cents(i[class="y">:unit_price]) * i[class="y">:qty] }.sum(Money.new(0))
  tax = subtotal * TAX_RATE
  {subtotal_cents: subtotal.cents, tax_cents: tax.cents, total_cents: (subtotal + tax).cents}
end

# Run both as plan tasks; validate only the ledger (floats can't even
# SIGN the contract - integer cents is a type, and types are promises)
orchestrator = Agentic:class="y">:PlanOrchestrator.new
float_task = Agentic:class="y">:Task.new(description: class="s">"float invoice", agent_spec: {class="s">"name" => class="s">"f", class="s">"instructions" => class="s">"w"}, payload: LINE_ITEMS)
ledger_task = Agentic:class="y">:Task.new(description: class="s">"ledger invoice", agent_spec: {class="s">"name" => class="s">"l", class="s">"instructions" => class="s">"w"}, payload: LINE_ITEMS)
orchestrator.add_task(float_task, agent: ->(t) { float_invoice(t.payload) })
orchestrator.add_task(ledger_task, agent: ->(t) { ledger_invoice(t.payload) })
result = orchestrator.execute_plan

floats = result.task_result(float_task.id).output
ledger = result.task_result(ledger_task.id).output

puts class="s">"MONEY DISCIPLINE (same invoice, two arithmetics)"
puts
puts format(class="s">"  %-12s %-28s %s", class="s">"", class="s">"float version", class="s">"ledger version")
puts format(class="s">"  %-12s %-28.14f %s", class="s">"subtotal", floats[class="y">:subtotal], Money.new(ledger[class="y">:subtotal_cents]))
puts format(class="s">"  %-12s %-28.14f %s", class="s">"tax", floats[class="y">:tax], Money.new(ledger[class="y">:tax_cents]))
puts format(class="s">"  %-12s %-28.14f %s", class="s">"total", floats[class="y">:total], Money.new(ledger[class="y">:total_cents]))
puts

Agentic:class="y">:CapabilityValidator.new(CONTRACT).validate_outputs!(ledger)
puts class="s">"  the ledger version signed its contract: integer cents, all"
puts class="s">"  non-negative, and the adds_up rule verified to the penny."
puts
float_cents = (floats[class="y">:total] * 100).round
puts class="s">"  now read the float column like an accountant: the subtotal ends"
puts format(class="s">"  in ...%s, because 0.1 x 3 is not 0.3 in binary - IEEE 754", format(class="s">"%.14f", floats[class="y">:subtotal])[-6..])
puts class="s">"  is already paying out interest. today it rounds to the"
puts class="s">"  right penny (#{float_cents}); at some other quantity or rate it won't,"
puts class="s">"  and the discrepancy will surface in a reconciliation report"
puts class="s">"  eleven months from now, assigned to whoever touched the code"
puts class="s">"  last. the discipline is three sentences: money is integer"
puts class="s">"  cents (a TYPE the contract can enforce - \"integer\class="s">" isn't"
puts class="s">"  pedantry, it's a tripwire); rounding is a NAMED policy applied"
puts class="s">"  at declared points (banker's, at multiplication), not an"
puts class="s">"  accident of printf; and the books must balance BY RULE"
puts class="s">"  (adds_up), not by hope. take my money - but count it in cents."