# Chapter 9 · Adopt it in ten minutes

Machine edition of https://agentc.consulting/agent-enhanced-development/chapter-9/adopt-it-in-ten-minutes
Part of Agent-Enhanced Development: https://agentc.consulting/agent-enhanced-development.md
Source: https://github.com/AgentC-Consulting/aed-conventions/blob/main/ADOPTION.md

## The moment

You have read eight chapters and you want the conventions in your repo today,
not next sprint. So you paste two lines into your agent. It installs the
plugin, writes a section into your `CLAUDE.md`, renames what the linter is sure
about, hands back the one name it is not sure about, and scaffolds a file from
a sentence you say out loud. Then it reports what it changed.

You read that report line by line and say yes. What you just confirmed is the
first entry in your own adoption log.

None of this is required to read the book. The book works with nothing
installed; the plugin is the part that puts the rules inside the tool your
agent already uses.

## The install: two lines, then a report you confirm

1. **The two lines.** A terminal runs the marketplace add and the install.
   Nothing about your code has changed yet; you have added three skills,
   three commands, and one advisory hook to the session.
2. **`/aed:adopt` writes the section.** The naming doctrine goes into your
   project's `CLAUDE.md`, where every agent reads it before it edits anything.
   The command reports which file it wrote and whether it added the section or
   updated one that was already there.
3. **`/aed:check` lints and triages.** It takes the paths you name, or works
   out your changed files from git, runs the linter, and splits what comes back
   in two: clear renames it applies for you, judgment calls it hands to you.
4. **`/aed:scaffold` turns a sentence into a file.** You give it one
   When-statement. It derives the class name, the `initialize` inputs, and the
   `perform` steps, checks every one of those names against the linter, and
   only then writes the file at the path the class name dictates.

Four stages. Three of them are the agent typing. The one that takes thought is
the judgment call in stage 3, which is the whole point of the ten minutes: the
tool does the mechanical part and stops at the part that is yours.

Stage 1, the two lines:

```
$ claude plugin marketplace add AgentC-Consulting/aed-conventions
$ claude plugin install aed@aed-conventions
aed@aed-conventions installed · 3 skills, 3 commands, 1 naming hook
```

Inside a Claude Code session the same two steps are
`/plugin marketplace add AgentC-Consulting/aed-conventions` and
`/plugin install aed@aed-conventions`. That third line is this book's shorthand
for what the plugin manifest actually ships: the skills `aed:naming`,
`aed:planning`, and `aed:process-managers`; the commands `/aed:check`,
`/aed:scaffold`, and `/aed:adopt`; and a `PostToolUse` hook that runs the
linter after every edit to a Ruby, Crystal, or Elixir file.

You need Claude Code with plugin support, and `ruby` on `PATH` for the linter
and the hook; macOS and most Linux distributions have it already. Without
`ruby` the three skills still work; only the linter-backed command steps and
the hook go quiet.

Stage 2 is one command, and this is the part of it that matters most: the
opening of the section `/aed:adopt` writes into your `CLAUDE.md`:

```
## AED conventions

This project follows Agent-Enhanced Development (AED) conventions —
conventions: https://github.com/AgentC-Consulting/aed-conventions

Name rules summary:
- Attributes of primitive types are short statements of intent
  (`first_name`, not `name`).
...
- Process managers are named from a "when"-statement and expose a single
  `perform` entry point that reads like pseudocode.
```

That last bullet is the one chapter 4 spends a whole chapter on, and chapter 3
holds the rest of the name rules in full. The section is a summary that lives
where an agent will read it, not a replacement for the chapters.

Stage 4 verifies before it writes. Before the scaffold puts a single character
in a file, it asks the linter about every name it derived:

```
$ ruby aed_lint.rb check-name --kind class Billing::ApplyLateFee
OK Billing::ApplyLateFee
$ ruby aed_lint.rb check-name --kind attribute how_many_days_late
OK how_many_days_late
$ ruby aed_lint.rb check-name --kind boolean has_the_late_fee_been_charged
OK has_the_late_fee_been_charged
```

Verify, then write, never the other way around. A name the linter flags gets
revised while it is still a proposal, which costs nothing, instead of after it
is spread across a repository, which costs a refactor.

## The report becomes the adoption log

Look at what the agent handed you. It is dated, because it happened just now.
It is itemized, one change per line. Each line names the rule that justified
it. That is not a chat message. That is an adoption-log entry that happens to
have been typed into a chat window.

So move it. The conventions repository keeps its own record this way, and says
what the record is for: "Every rule change, publication, and license event
gets a dated entry here at the time it happens." At the time it happens is the
load-bearing part. A log written at the end of the quarter is a
reconstruction; a log written as the change lands is evidence.

The repository is honest about the difference, and marks it. Entries that describe
practice predating the public repository carry a *(reconstructed)* tag and the
note that they are "honest reconstructions, not contemporaneous records." Use
the same tag in your own log the moment you backfill anything. An entry that
says when it was written down, separately from when it happened, is worth more
than one that quietly blurs the two.

## What you confirm, and what you do not

The checklist is five lines, and each one is a yes:

1. **The plugin is installed.** Three skills, three commands, one hook, in
   this repository's sessions.
2. **The `CLAUDE.md` section is written.** Added, or updated in place. The
   command is idempotent: run it again next month and you get one section, not
   two.
3. **The clear renames were applied.** Every reference to the old name updated
   in the same pass, so nothing is left broken.
4. **The judgment call is yours.** The agent listed the current name, the
   finding, and its recommended name, and then waited. It did not rename a
   class across your repository on its own.
5. **The scaffolded file exists.** At the snake_case path its class name
   dictates, with every name checked before it landed.

What you do not confirm is a gate, because there is not one. Nothing in the
plugin failed a build or blocked an edit at any point in those four stages.
The repository says so in the entry that added the plugin: "Enforcement is
advisory only, by design — the same posture as the written conventions. The
linter always exits 0, the hook never blocks an edit, and every finding is a
suggestion for an agent or human to accept or reject, never a gate."

If you want a gate you have to ask for one. `--strict` turns advisory findings
into failures, which is useful in CI and nowhere else. The default is the
opposite on purpose: a rule that can veto a commit gets argued with; a rule that
suggests gets adopted.

## The rules this chapter settles

- Install with `/plugin marketplace add AgentC-Consulting/aed-conventions` then
  `/plugin install aed@aed-conventions` inside a session; the shell equivalents
  are `claude plugin marketplace add …` and
  `claude plugin install aed@aed-conventions`.
- Enforcement is advisory only, by design. "Nothing in this plugin fails a build
  or blocks an edit. The hook, the commands, and `check-name` all report
  findings; applying them is always a judgment call made by you or by the agent
  acting on your behalf."
- "`--strict` turns advisory findings into failures (useful for a CI gate);
  without it, the linter always exits 0 — it informs, it does not block."
- `ruby` on `PATH` is what the linter and the edit-time hook need. "Without
  `ruby`, the skills still work — only the linter-backed command steps and the
  hook no-op." And if `ruby` is missing, `/aed:check` is told to "say so plainly
  and stop — do not attempt to reimplement the linter's rules by hand."
- The whole linter CLI is `--format text|json`, `--strict`,
  `check-name --kind boolean|collection|attribute|class|method`, and `--hook`.
  There is nothing else to learn.
- `/aed:adopt` is idempotent: "if the section already exists, update it in place
  rather than adding a duplicate", and it ends by reporting "whether the section
  was added or updated, and the `CLAUDE.md` path."
- `/aed:check` triages in two piles. Clear renames are applied directly,
  "updating every reference to the old name in the same pass so nothing is left
  broken." Judgment calls are not: "List each one with the current name, the
  finding, and your recommended name, and let the user decide."
- `/aed:scaffold` verifies before it writes: "Revise any name the linter flags
  before it ever lands in the file." Verify, then write, never the other way
  around.
- A scaffold starts from a sentence. "A well-formed statement always starts with
  'when': it names the qualifying information the process needs, and the
  operation(s) it performs once that information is available."
- Plugin versioning is separate from the conventions' own. The plugin versions
  independently of the `v1.x` doctrine tags; "a plugin release carries no claim
  about the state of the conventions, and a conventions release carries no claim
  about the plugin." Write both numbers in your log entry and let them move
  apart.
- The adoption log is the record: "Every rule change, publication, and license
  event gets a dated entry here at the time it happens." Backfilled entries are
  marked *(reconstructed)*.

## Why it matters to the agent

An agent reads `CLAUDE.md` before it edits anything. Put the naming doctrine
there and it is in the agent's first window, every session, for free, forever.
That is what `/aed:adopt` buys, and it is why the section is written into the
repository instead of pasted into a conversation: a conversation ends, and the
next agent starts with nothing. Chapter 1 makes the token-window argument in
full.

The hook works on a different clock. It re-states the rule at the moment of the
edit, the only moment the rule is actionable, and then gets out of the way.

The adoption log serves the agent after that. Next month, a different session
opens your repository and finds a class called `Billing::ApplyLateFee` where
the git history shows a `LateFeeService`. Without the log it has to guess
whether that rename was policy or an accident. With the log it reads the dated
line, the rule that justified it, and the fact that a human confirmed it, and
it writes the next file the same way.

The repository's own evidence points at the bundle, not at any one piece of
it. In the pet-tracker build-off of 2026-08-11, the two arms differed by the
whole AED bundle (statement-shaped names, the `CLAUDE.md` section, and the
advisory hook together), which is exactly what these four stages install.
Across two runs per arm with a small model, the arm without AED shipped zero
working user journeys of five, twice; the arm with it shipped two and four.
That is a demonstration, not a study, and the repository publishes its caveats
alongside it: one task, one codebase, one model, one machine. The earlier
snippet-scale benchmark is the same shape of claim: AED-style Crystal scored
60/60 against conventional style's 54/60 when Claude Haiku answered blind and
a blind grader scored it, published as "a directional signal consistent with
the hypothesis, not proof of it." Take both as what they say they are.

## Before and after

The repository held this file first, and a `CLAUDE.md` with nothing in it about
how names are chosen.

```crystal
# src/services/late_fee_service.cr
class LateFeeService
  def initialize(invoice, days_late)
    @invoice = invoice
    @days_late = days_late
  end

  def process
    if @days_late >= 30
      @invoice.charge(@invoice.amount * 0.05)
      CustomerMailer.late_fee(@invoice).deliver
    end
  end
end
```

Stage 3 runs, and this is the linter's own output on the two changed files,
including the exit code:

```
$ ruby aed_lint.rb src/models/customer.cr src/services/late_fee_service.cr
src/models/customer.cr:2: [AED-N4 info] attribute `name` is a single word; the conventions ask attributes to be short statements of purpose — first_name / last_name / full_name
src/models/customer.cr:3: [AED-N4 info] attribute `email` is a single word; the conventions ask attributes to be short statements of purpose — email_address
src/models/customer.cr:4: [AED-N3 warn] enumerable attribute `subscriptions` does not say it holds a collection — list_of_subscriptions / collection_of_subscriptions / array_of_subscriptions
src/models/customer.cr:5: [AED-N2 warn] boolean attribute `payment_method_present` does not read as a yes/no question — is_this_customer_payment_method_present / has_been_payment_method_present
$ echo $?
0
```

Four findings on `Customer`, all of them clear renames, all applied with their
references updated in the same pass. And nothing at all on
`late_fee_service.cr`. The linter is line-based and narrow, and the old class
defines `process` rather than `perform`, so the process-manager rules never
fired.

That silence is the interesting part. `LateFeeService` is the name chapter 3
would change, and the linter did not raise it. Your agent did, out of the
`aed:naming` skill, and then handed it back as the judgment call:

> `LateFeeService` → `Billing::ApplyLateFee`. A class name should be a short
> statement of the process performed, namespaced to its feature. Renaming
> touches every caller. Your call.

You say yes. Then one sentence does the rest:

`/aed:scaffold "When an invoice passes its grace period, charge the late fee and tell the customer"`

```crystal
# src/billing/apply_late_fee.cr

# 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

  # -- What you can read afterward --

  # Whether the fee actually landed on the invoice.
  property has_the_late_fee_been_charged : Bool = false

  def initialize(@invoice : Invoice, @how_many_days_late : Int32)
  end

  # -- 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)
    self.has_the_late_fee_been_charged = true
  end

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

This is chapter 3's file, arrived at from the other direction. Chapter 3 showed
you the finished thing and asked you to read it four ways; this chapter shows
how it got written in ten minutes without anybody typing it by hand. The one
addition is `has_the_late_fee_been_charged`, which pays off two rules at once:
booleans are phrased as questions, and a process manager that is used for
anything beyond a single return value exposes read-only public accessors for
what it did.

And here is the confirmed checklist, written into `ADOPTION.md` in your own
repository. The version numbers are the ones you actually installed, so check
yours rather than copying these:

```markdown
## 2026-09-20 — AED adopted

- Installed `aed@aed-conventions` (plugin 0.1.2, conventions v1.1.0-rc.1):
  3 skills, 3 commands, 1 advisory naming hook.
- `/aed:adopt` added the "AED conventions" section to `CLAUDE.md`.
- `/aed:check` linted 2 changed files: 4 findings, 4 renames applied on
  `Customer`. The linter raised nothing on the service file; the agent raised
  the class name from the naming doctrine, and the owner confirmed it:
  `LateFeeService` → `Billing::ApplyLateFee`.
- `/aed:scaffold` wrote `src/billing/apply_late_fee.cr` from the When-statement,
  every derived name checked with `check-name` before the file was written.
- Enforcement stays advisory: the lint run exits 0, the hook never blocks an
  edit. No `--strict` in CI yet.
```

Count the ten minutes against the four stages. Two lines to install. One command
to write the section. One command to lint and triage, of which only the judgment
call needed you. One sentence to scaffold the file. The rest of the time is
reading the report, and reading the report is the part that turns an install
into a record.

Next: back to the book. Nine chapters, one billing example, one set of
conventions now sitting in your repository where your agent reads them.
