Adopt it in ten minutes

You want the conventions in your repository today, so you paste two lines into your agent, and what it hands back becomes the first entry in your own adoption log. Scroll, or press a step.

The two lines. A terminal runs the marketplace add and the install. Nothing in your code has changed yet.

It writes the section. The naming doctrine goes into your CLAUDE.md, where every agent reads it before it edits anything.

It lints and triages. What comes back splits in two: clear renames it applies for you, judgment calls it hands to you.

It writes the file from a sentence. One When-statement gives it the class name, the initialize inputs and the perform steps.

zsh · aed-conventions
$ 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
> /aed:adopt
## 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.

CLAUDE.md · "AED conventions" section added
> /aed:check
src/models/customer.cr · 4 findings, 4 clear renames applied, every reference updated in the same pass
  name → first_name
  email → email_address
  subscriptions → list_of_subscriptions
  payment_method_present → is_this_customer_payment_method_present
src/services/late_fee_service.cr · nothing at all
1 judgment call, handed back:
  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.
> /aed:scaffold "When an invoice passes its grace period, charge the late fee and tell the customer"
$ 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
wrote src/billing/apply_late_fee.cr
$ 

ADOPTION.md · your repository

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: LateFeeServiceBilling::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.
  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. 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.It listed the current name, the finding, and its recommended name, and then waited.
  5. The scaffolded file exists.At the snake_case path its class name dictates, with every name checked before it landed.

A block set into the gap it fits, and the dated line it leaves behind

The turn of the chapter

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

You confirm five facts. There is no gate, because nothing here can block you.

The checklist above is five lines, and each one is a yes. 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

The plugin suggests. You decide, and the log remembers.

  • 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.
    optional, start to finish
  • Two lines install it.Inside a session, or in a shell; the same marketplace and the same package either way.
    /plugin marketplace add AgentC-Consulting/aed-conventions
    /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."
    exit 0, always
  • A gate is something you ask for."--strict turns advisory findings into failures (useful for a CI gate); without it, the linter always exits 0 — it informs, it does not block."
    --strict
  • 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 it is missing, /aed:check is told to "say so plainly and stop — do not attempt to reimplement the linter's rules by hand."
    ruby --version
  • The whole linter CLI fits on one line.There is nothing else to learn.
    --format text|json
    --strict
    check-name --kind boolean|collection|attribute|class|method
    --hook
  • /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:adopt
  • /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:check
  • /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.
    check-name --kind class
  • 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."
    /aed:scaffold "When …, then …"
  • Plugin versioning is separate from the conventions' own."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.
    plugin 0.1.2
    conventions v1.1.0-rc.1
  • 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).
    ADOPTION.md
The gold outline is the context window; the tiles inside and around it are tokens. It slides one token at a time, and what falls outside it the model cannot read.

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 same billing process, before and after.

billing · before and after
late_fee_service.cr · before
1# src/services/late_fee_service.cr
2class LateFeeService
3 def initialize(invoice, days_late)
4 @invoice = invoice
5 @days_late = days_late
6 end
7
8 def process
9 if @days_late >= 30
10 @invoice.charge(@invoice.amount * 0.05)
11 CustomerMailer.late_fee(@invoice).deliver
12 end
13 end
14end
apply_late_fee.cr · after
1# src/billing/apply_late_fee.cr
2
3# Applies a late fee to an invoice that has gone past its grace period, then tells the customer.
4class Billing:::ApplyLateFee
5 # -- What this needs --
6
7 # The invoice being charged. It knows its customer, its amount, and its due date.
8 property invoice : Invoice
9
10 # How many days past the due date the invoice is today.
11 property how_many_days_late : Int32 = 0
12
13 # -- Company policy --
14
15 # Days past due before any fee applies.
16 property grace_period_in_days : Int32 = 30
17
18 # The fee, as a share of the amount owed.
19 property late_fee_rate : Float64 = 0.05
20
21 # -- What you can read afterward --
22
23 # Whether the fee actually landed on the invoice.
24 property has_the_late_fee_been_charged : Bool = false
25
26 def initialize(@invoice : Invoice, @how_many_days_late : Int32)
27 end
28
29 # -- How it runs --
30
31 def perform
32 stop_unless_the_invoice_is_overdue
33 charge_the_late_fee
34 tell_the_customer
35 end
36
37 # Nothing happens inside the grace period.
38 private def stop_unless_the_invoice_is_overdue
39 return if how_many_days_late < grace_period_in_days
40 end
41
42 # The fee is a share of the amount owed, charged to the same invoice.
43 private def charge_the_late_fee
44 invoice.charge(invoice.amount * late_fee_rate, kind: :late_fee)
45 self.has_the_late_fee_been_charged = true
46 end
47
48 # The customer gets the notice by email.
49 private def tell_the_customer
50 CustomerMail.late_fee(invoice).deliver
51 end
52end

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

zsh · aed_lint.rb
$ 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:

LateFeeServiceBilling::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". 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.

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. The entry that record becomes is the dated one you confirmed at the top of this page, written into ADOPTION.md in your own repository; the version numbers there are the ones you actually installed, so check yours rather than copying them.

Reading with an agent? Hand it the machine edition of this chapter. The source is the adoption guide in the conventions repository.