# Chapter 1 · Why agents need conventions

Machine edition of https://agentc.consulting/agent-enhanced-development/chapter-1/why-agents-need-conventions
Part of Agent-Enhanced Development: https://agentc.consulting/agent-enhanced-development.md
Source conventions: https://github.com/AgentC-Consulting/aed-conventions/blob/main/01_why_models_need_this.md

## The moment

My agent stopped reading before it reached the part that mattered. It scrolled
my file, answered confidently about what the code does, and never got as far as
the grace period, the one line that decides whether a customer gets charged.
Not because the line sat off the end of the file. Because by the time it
arrived it was a bare `30` in the middle of a guard, and nothing anywhere in
that file said what 30 was for.

This chapter is one file read twice. Nothing gets rewritten, nothing gets
refactored, and no new framework shows up. You read both files to the end and
watch where the meaning arrives.

## The window: one file, read twice

Two editor panes sit side by side. The file as most of us write it is on the
left, the same job written the AED way is on the right, and a meter above them
stands in for the agent's token window. Reading down the page drives the meter
through four stages. The whole scene reads with JavaScript switched off; the
meter is decoration.

1. **A model does not read your file the way you do.** It reads a group of
   tokens at a time, shifting down as it processes, and what it understands at
   any moment is whatever is inside that group. The meter above the panes is
   that group.
2. **The left pane fills with ceremony.** A class named for a category rather
   than a job, an `initialize`, two assignments, a method called `process`. By
   the time the window has traveled that far it has spent itself on plumbing,
   and the number 30 arrives unlabeled inside a conditional. The window
   contains the number. It does not contain the policy.
3. **The right pane spends its first window differently.** One sentence saying
   what the class is for, then what it needs, then the company's two numbers
   under their own heading: `grace_period_in_days : Int32 = 30` and
   `late_fee_rate : Float64 = 0.05`, each with a line of plain English above
   it. A reader who sees only that much already knows there is a grace period,
   how long it is, and that it is a policy someone chose.
4. **What the meter measures is not lines remaining. It is meaning delivered
   per window.** The AED file is the longer of the two, 41 lines against 22,
   and it still arrives first, because the ordinary file means almost nothing
   until you have read all of it and assembled the intent in your own head.

The file as most of us write it:

```crystal
class LateFeeService
  def initialize(invoice, days_late)
    @invoice = invoice
    @days_late = days_late
  end

  def process
    if @days_late >= 30
      fee = calculate_fee(@invoice.amount)
      @invoice.charge(fee)
      send_notification(@invoice)
    end
  end

  def calculate_fee(amount)
    amount * 0.05
  end

  def send_notification(invoice)
    CustomerMailer.late_fee(invoice).deliver
  end
end
```

The same job, written the AED way:

```crystal
# Applies a late fee to an invoice that has gone past its grace period, then tells the customer.
class Billing::ApplyLateFee
  # -- What this needs --

  # The invoice being charged. It knows its customer, its amount, and its due date.
  property invoice : Invoice

  # How many days past the due date the invoice is today.
  property how_many_days_late : Int32 = 0

  # -- Company policy --

  # Days past due before any fee applies.
  property grace_period_in_days : Int32 = 30

  # The fee, as a share of the amount owed.
  property late_fee_rate : Float64 = 0.05

  # -- How it runs --

  def perform
    stop_unless_the_invoice_is_overdue
    charge_the_late_fee
    tell_the_customer
  end

  # Nothing happens inside the grace period.
  private def stop_unless_the_invoice_is_overdue
    return if how_many_days_late < grace_period_in_days
  end

  # The fee is a share of the amount owed, charged to the same invoice.
  private def charge_the_late_fee
    invoice.charge(invoice.amount * late_fee_rate, kind: :late_fee)
  end

  # The customer gets the notice by email.
  private def tell_the_customer
    CustomerMail.late_fee(invoice).deliver
  end
end
```

Same domain, same customer, same money. The difference is where in the scroll
the meaning shows up.

## Sources for this section

- *"Naming conventions are one of the ways that drive the agent enhanced
  development workflow. The reason for this is that AI models use token
  windows to understand the context of things."*
  Everything else in this book follows from the window. Start here or the rest
  reads like taste.
- *"when the AI model is scanning through the prompt, it’s going to read a
  group of tokens at a time, shifting down as it processes."*
  Your file is never read whole. It is read in overlapping slices.
- *"This association is how the relationship of a flow of words is established
  and influences the direction that the model computes. This is why a naming
  convention needs to be very consistent."*
  Inconsistent names break the association the model is building, slice by
  slice.
- *"Prefer the form that reads like a plain statement of intent. Reach for
  shorthand only when it makes the intent clearer, never just shorter."*
  The guiding rule of the whole convention set, and the tie-breaker for every
  argument the rest of the book could start.
- *"Code is read and modified far more often than it is written — and in an
  agent-driven codebase, 'read' now includes every agent that will ever touch
  the file."*
  The reader you are writing for is now mostly machine, and it reads the file
  again every single session.
- *"Clever, compressed syntax saves the author a few keystrokes and costs
  every later reader, human or machine, a re-parse. AED optimizes for the
  reader."*
  A re-parse costs the model window, and window is the scarce thing.
- *"The test for every line you write: does a reader who has never seen this
  code understand the intent on first pass? If they would have to mentally
  execute it, expand it."*
  The one test you can apply without consulting anything. `@days_late >= 30`
  fails it; `how_many_days_late < grace_period_in_days` passes it.
- *"Read the spine in order — 01 first, because it explains why the rest
  exists."*
  The conventions' own instruction to an agent, and the reason this is chapter
  1 rather than an appendix.

## Why it matters to the agent

Here is the worked example the conventions use. Take an ordinary sentence:

`I want to create a new user and assign them to an account.`

Tokenized with Llama 3, that sentence becomes fourteen tokens: `'I'`,
`' want'`, `' to'`, `' create'`, `' a'`, `' new'`, `' user'`, `' and'`,
`' assign'`, `' them'`, `' to'`, `' an'`, `' account'`, `'.'`. The leading
spaces inside the quotes are real: a space is part of a token.

Now read it the way a model does. Using an eight-token window that shifts four
tokens at a time, so the slices overlap, the sentence is not read once. It is
read three times over:

- `'I want to create a new user and'`
- `' a new user and assign them to an'`
- `' assign them to an account.'`

A real window is far larger than eight tokens, but the shape is the same at any
size. Meaning is assembled from overlapping groups, and the model's next
computation is steered by the group it is currently holding. That is the
association the conventions describe in the rules above, and it is why a naming
convention has to be consistent rather than merely reasonable.

Apply that to the two files. `days_late` only carries its meaning if the reader
already knows the unit and the subject, days of what and late for what, and
both of those live somewhere else in the file, in a different window.
`how_many_days_late` carries the unit and the subject in the name itself, so
every window that contains the name contains the meaning. The same is true of
the policy. A bare `30` means whatever the surrounding window says it means;
`grace_period_in_days : Int32 = 30` means the same thing in every window it
ever appears in, including a window that contains nothing else from the file.

That is the whole argument, and it is the argument chapter 3 points back here
for: names are not labels for humans, they are the units the window carries.

## What one small model measured

We ran a small comprehension benchmark to see whether this shows up in
practice. Ten pairs of Crystal snippets, one AED variant and one conventional
variant each, checked for identical behavior first so the test measured style
and not logic. Claude Haiku answered three probes on each variant blind, one for intent, one
for modification, one for defect, and a blind grader scored the answers.

AED scored 60 out of 60. Conventional scored 54. In the report's words:

> AED never lost a probe; conventional lost points on 3 of 10 pairs,
> concentrated in defect-finding (16/20) and intent (18/20) probes, while
> modification probes tied (20/20 each).

The shape of that result matters more than the total:

> The style seems to matter when the model must *infer* purpose or *notice* an
> edge case, not when it is executing instructions.

Which is exactly the grace period. Tell an agent to change the fee to six
percent and it will manage that in either file. Ask it what the file is *for*,
or whether an invoice twenty-nine days overdue gets charged, and the style
starts to decide the answer.

Now the limits, printed here rather than buried, because the report prints
them:

> A 6-point gap on n=10 with 7 ceiling ties is a directional signal consistent
> with the hypothesis, not proof of it.

One small model, one run per probe, no variance estimate, a model doing the
grading, snippets rather than a codebase, and pairs authored by the people who
authored the conventions. Treat it as a reason to try this on your own code,
not as a number to quote at anyone.

And the cost, which the same report names as a threat to its own validity:

> AED variants are consistently longer (e.g. p10: 38 vs 21 lines).

That is true of our two files as well: 41 lines against 22. AED spends lines so
that a reader spends fewer windows on inference. If your bottleneck is typing,
that trade is a loss. If your bottleneck is an agent that has to work out what
your code is for before it can safely change it, it is the trade the whole
convention set is built on.

## Plain attributes, before and after

Between the two files above, one thing moved and nothing else did. The policy
left the method body and became a named property with a sentence above it. The
code below holds one more class twice: the plain attributes first, the stated
ones second.

```crystal
# before
class Customer
  property name : String
  property email : String
  property subscriptions : Array(Subscription) = [] of Subscription
end

# after
class Customer
  property first_name : String
  property last_name : String
  property email_address : String
  property list_of_all_active_subscriptions : Array(Subscription) = [] of Subscription
  property is_this_an_enterprise_customer : Bool = false
end
```

`subscriptions` needs the surrounding file to tell you it is a list and which
subscriptions it holds. `list_of_all_active_subscriptions` tells you inside the
name. Chapter 3 takes these naming rules apart one at a time; this chapter only
claims the reason they exist.

Next: Chapter 2 · Vibe coding as a squad (one agent reading one file is the
easy case; the real one is several agents writing in the same codebase at once,
each reading what the others just wrote).
