Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Working with a coding agent

melee new writes an AGENTS.md that tells an agent the rules. The two it needs most are that melee dev is not the compiler, and that a clean compile is not a working app.

An agent that has written Rails or Sinatra will be productive in a melee app within a few minutes and will also, left to itself, reach for four things that do not exist here: a gem, a background job, a class that builds methods at runtime, and a Date. None of those fail in an obvious place. Most of them fail at compile time with a message about a type, and some of them compile and behave differently from how they behaved in development.

So the useful instructions are not a summary of the API — the agent can read that. They are the handful of things that are true of melee and of nothing else the agent has seen.

What melee new writes

Every app scaffolded since this was added has an AGENTS.md in its root, named after the app:

notes/
  AGENTS.md    the dialect, the loop, and what a green build does not prove
  README.md    the same directory, for a person

Agents that look for AGENTS.md, CLAUDE.md or an equivalent pick it up with no prompting. If your app predates it, or you keep your instructions somewhere else, melee new a throwaway app in /tmp and copy the file out — or write your own from the three sections below.

An AGENTS.md you already have is never overwritten. melee new . in a directory that has one keeps yours and says so.

The two instructions that matter

melee dev is CRuby, not the compiler. It is the fast loop, and it is not the truth. An agent that runs melee dev, makes a request, sees a 200 and reports success has proved less than it thinks: the deployed app is a binary built by Spinel, and the two disagree about more than you would like. Tell it to finish with melee check, and with melee test --both when there is a test — the --both half compiles the test and diffs it against the CRuby run, which is the only cheap way to catch a dialect trap before a user does.

A clean compile is not a working app. Wrong arity, calling a method on nil, and Integer + String all compile and raise when they run, and so does a misspelt method reached through an explicit receiver — db.frist(...), user.nmae — in at least some shapes. An agent that reasons “it compiled, so it works” will hand you a broken route with real confidence. Tell it to exercise the route it changed, and treat a green build as the start of checking rather than the end of it.

Both of these are in the scaffolded AGENTS.md. They are worth repeating in your own prompt when you ask for something substantial, because they are the two beliefs an agent brings from every other Ruby codebase.

The reference to point it at

The library’s documentation ships with the library, so it is on disk at the version the app actually builds against, after the first melee dev, melee check or melee test:

.melee/stdlib/docs/llms.txt      the index, and the dialect in ten rules
.melee/stdlib/docs/dialect.md    what compiles and what does not, with the workaround for each
.melee/stdlib/docs/reference.md  every method with its signature

That beats pointing at the web: it cannot drift from the toolchain, and it works with no network. But it only exists after a build, and plenty of tools want a URL, so the manual publishes two indexes in the llms.txt shape:

https://melee.ideasasylum.com/reference/llms.txt   the app-facing surface and nothing else
https://melee.ideasasylum.com/llms.txt             the whole manual, including the operator pages

Start an agent on reference/llms.txt. The whole manual is a lot of tokens to spend on an app that needs one route and a migration, and most of it is about running a server rather than writing an app.

What to expect it to get wrong

Not a complete list — the dialect is — but these are the ones that come up again and again, in rough order of how often:

  • A gem. There is no bundler and no Gemfile. The require list is short and closed; anything outside it has to be written by hand or done without.
  • A background job. No threads, no queue, no async. Work that outlives a request goes in a durable object with a timer, which is a genuinely different shape and worth reading before asking for one.
  • Date, Time.parse, DateTime. Timestamps are Unix seconds or YYYY-MM-DD strings, and the arithmetic is yours.
  • A mixed container. An Array or Hash that holds two shapes, or a method that returns a String on one path and nil on another, poisons type inference — and the error surfaces somewhere else entirely, often as a NoMethodError on a method that plainly exists.
  • row[:name]. Rows are Hashes keyed by String.
  • A rescue through an alias. rescue HTTP::Error never matches; the class has to be spelled in full.
  • Editing a migration that has already run. Migrations are files applied in name order, once.

Reviewing what it wrote

The same three commands you would run yourself, in this order:

melee check          # does it compile
melee test --both    # does it behave the same compiled as interpreted
melee dev            # does the route actually do the thing

melee check is the one an agent skips, and it is the one that catches the dialect. Ask for its output rather than for a summary of it — the diagnostics name the file and line, and a summary of a compile error is worth much less than the compile error.