Language Guide
Butter is an intent specification language for AI agents.
AI integration tip: teach your AI the Butter language before asking it to help write a spec. Give your AI agent this prompt:
Learn the Butter Specification Language and its syntax from the language guide
at https://butter-jet.vercel.app/language.html
Then help me express application intent.
Core Philosophy
Butter is an intent specification language for AI agents. A Butter spec states what software must do, what must not happen, which constraints apply, and what outcomes to expect.
Use Butter when you want an AI agent to build software from precise, reviewable requirements instead of a loosely worded prompt.
Workflow
1. Write the application intent as a Butter spec
2. Compile the spec into a prompt (.prompt.md)
3. Give the prompt to an AI agent with your implementation context
4. Review and test the result
Intent and Implementation Context
Keep application intent (the Butter spec) separate from technology decisions (the implementation context). Both go to the AI agent, but they serve different purposes.
| Butter spec: intent | Implementation context: technology |
|---|---|
| Business rules, constraints, and invariants | Programming language and framework |
| Validation requirements | Database technology and libraries |
| Workflows and ordered actions | Infrastructure and deployment environment |
| Expected outcomes | Architecture choices |
| HTTP endpoint contracts | Coding conventions, project structure, technology docs |
The same spec combined with different implementation context produces different applications:
Butter specification
+
"Implement using Laravel + Vue"
↓
Laravel/Vue application
Butter specification
+
"Implement using Django + React"
↓
Django/React application
The spec does not change, because the application's intent has not changed.
Syntax
A spec is one app block plus any number of feature and endpoint blocks. The tree shows what each block may contain.
# Root Application Specification
app MyApp
├── description <string>
├── version <string>
├── rules
│ └── <string>
├── feature <name>
└── endpoint <name> <route>
# Subsystem Feature Block Specification
feature CreateTask
├── description <string>
├── version <string>
├── params
│ └── <name> <type>
└── actions
└── <string>
└── enforce <string>
# HTTP Endpoint Contract
endpoint StoreTodo "/todo"
├── description <string>
├── version <string>
├── method <string>
├── params
│ └── <name> <type>
├── responses
│ └── <name>
│ └── <name> <type>
├── actions
│ └── <string>
│ └── enforce <string>
└── returns
└── <code> <ResponseName|string>
Formatting rules
- Indentation: exactly 2 spaces per nesting depth (depth 0 = 0 spaces, depth 1 = 2, depth 2 = 4, depth 3 = 6).
- Comments: start with
#or//. - Layout: the tree shows ownership, not indentation. Write
featureandendpointblocks at depth 0 after theappblock. - Blank lines: required above every
featureandendpoint(except the first block in the file) and above therulesblock. If comments sit directly above a block, the blank line goes above the comments. - Strings: descriptions, versions, rules, actions, enforce expressions, routes, and methods are quoted.
- Names: feature, endpoint, and response names are unquoted CamelCase. Param and response field names are snake_case.
- Unique names: feature names must be unique, and endpoint names must be unique. Two endpoints cannot share a name even if their routes differ.
What Butter Can Express
Every kind of application intent maps to an existing construct. Nothing here adds syntax.
| To express | Use |
|---|---|
| What the application does; workflows | feature with ordered actions |
| Business rules and invariants that apply everywhere | rules under app |
| A constraint on one step | enforce under an action |
| Valid input and validation | params, plus actions or enforce that reject invalid input |
| What must not happen (boundaries) | rules, actions, or enforce, written as prohibitions or rejections |
| HTTP interfaces | endpoint with params, responses, actions, and returns |
| Expected outcomes | actions and returns |
Rules
The rules block lives under app and declares application-wide business constraints and invariants, including things that must never happen.
app OrderService
description "Creates and manages customer orders"
version "1.0.0"
rules
"Customers may only access orders they own"
"An order cannot be charged for more than its calculated total"
"Operations that would create duplicate orders must be rejected"
"Deleted orders must not be returned by list operations"
How they work
- Place
rulesunderapp, afterdescriptionandversion. - Each rule is a quoted string on its own line, indented 4 spaces.
- Rules apply to every feature and endpoint in the spec, not to a single action. For a constraint on one action, use
enforce. - Do not use rules to choose technology (
"Use Laravel","Use PostgreSQL"). That belongs in the implementation context.
Features
A feature declares an application capability: the inputs it accepts and the behavior it provides.
feature ProcessPayment
description "Validates and records a payment"
version "1.0.0"
params
order_id string
amount double
payment_method enum["card", "bank_transfer"]
actions
"Validate the payment amount"
"Verify the payment method is allowed"
"Record the payment outcome"
Feature rules
- Names are CamelCase and may contain letters, digits, underscores, and hyphens.
- Everything under a feature must be indented.
- Valid keywords inside a feature are
description,version,params, andactions. - A feature can be empty: zero params and zero actions is valid.
Parameters
Params declare the inputs a feature or endpoint accepts. Each parameter is a name followed by a type, on its own line.
params
title string
priority enum["low", "medium", "high"]
limit integer
Types
| Type | Example | Notes |
|---|---|---|
string | name string | Default when type is omitted. |
integer | count integer | Whole numbers. |
double | amount double | Floating-point values. |
boolean | active boolean | true or false. |
enum[...] | mode enum["a", "b"] | One of a fixed set of quoted, unique values. |
array[...] | tags array[string] | A list of one element type: string, integer, double, or boolean. |
Parameter names are snake_case and must be unique within a feature or endpoint.
Actions
Actions describe the ordered behavior an implementation should perform. Each action is a quoted string.
actions
"Validate title is not empty"
"Assign a unique identifier to the new task"
"Set the creation timestamp"
"Notify the assigned user"
"Schedule a reminder for the due date"
How they work
- Sequential. Steps run in declaration order.
- Discrete. Keep each action to one behavior. Split complex work into multiple actions.
- Intent-focused. Describe the required behavior, not a preferred library, framework, or database. The implementation context decides how each action is realized.
Enforce
An action can have enforce expressions as indented children. Each states a condition that must hold for the action to succeed.
"Validate account balance"
enforce "The source account must retain its required minimum balance"
"Charge payment method"
enforce "The charge amount must not exceed the order total"
enforce "The payment method must be verified before charging"
The action describes the behavior; enforce states what must remain true. Use rules for app-wide invariants and enforce for constraints on a single action.
Endpoints
An endpoint describes an HTTP interface contract: route, method, parameters, response schemas, ordered behavior, and return mappings.
app OrderService
description "Creates and retrieves customer orders"
version "1.0.0"
endpoint CreateOrder "/api/orders"
description "Validates and creates an order"
version "1.0.0"
method "POST"
params
customer_id string
currency string
dry_run boolean
responses
OrderSuccessPayload
order_id integer
total_amount double
currency string
actions
"Validate the customer identifier"
"Calculate the order total"
"Create the order with a unique identifier"
returns
201 OrderSuccessPayload
200 "Dry run completed"
400 "Invalid order data"
500 "Order creation failed"
Endpoint rules
- The route goes in the header as a quoted string:
endpoint Name "/path". The parser rejects an unquoted or missing route. methodis required and must be one ofGET,POST,PUT,DELETE, orPATCH.- Valid keywords inside an endpoint are
description,version,method,params,responses,actions, andreturns.
Feature vs Endpoint: Usefeaturefor application behavior. Useendpointfor an HTTP interface contract. They sit side by side underapp.
Responses
The responses block defines named response schemas. Each response has a CamelCase name and typed fields.
responses
OrderSuccessPayload
order_id integer
total_amount double
currency string
line_items array[string]
ValidationError
error string
details string
Response names must be unique within an endpoint, and field names are snake_case. Every response name used in returns must be defined here.
Returns
The returns block maps HTTP status codes to response schemas or string payloads.
returns
201 OrderSuccessPayload
200 "Dry run completed"
400 "Invalid order data"
404
500 "Order creation failed"
Format
| Syntax | Meaning |
|---|---|
201 OrderSuccessPayload | Status 201, body matches the OrderSuccessPayload schema from responses. |
400 "Invalid order data" | Status 400, body is this string. |
404 | Status 404, no body. |
Status codes must be integers from 100 to 599 inclusive. Use standard HTTP status codes for meaningful mappings.
Structural Rules
A quick reference for what the compiler checks and which fields each block expects.
Compiler checks
- Exactly one
appper spec. - Unique feature names, endpoint names, param names, and
enumvalues. - Valid param types and HTTP methods.
- Status codes are integers from
100to599. - Every response name used in
returnsis defined in the same endpoint'sresponses.
Fields per block
Only "Required" fields are enforced. "Recommended" and "Optional" are convention and intent; the compiler does not require them.
| Block | Required | Recommended | Optional |
|---|---|---|---|
app | — | description | version, rules, feature (zero or more), endpoint (zero or more) |
feature | — | description, actions (at least 1) | version, params |
endpoint | Route in header, method | description, actions (at least 1), returns (at least 1) | version, params, responses |
Full-Stack Example — Task Manager Intent
This example specifies a full-stack application's intent without selecting a technology stack. It combines app rules, features, and HTTP endpoints using only existing Butter syntax.
app TaskManager
description "Manages tasks for individual users"
version "1.0.0"
rules
"Users may only access tasks they own"
"A task cannot be completed until it is assigned to a user"
"Operations that would create duplicate tasks must be rejected"
"Archived tasks cannot be modified or deleted"
feature CreateTask
description "Creates a task with a title, priority, and optional due date"
version "1.0.0"
params
title string
description string
priority enum["low", "medium", "high", "urgent"]
due_date string
assignee_id string
actions
"Validate title is not empty"
"Validate assignee_id identifies an existing user"
"Validate due_date is a valid future date when provided"
"Create a task with a unique identifier"
"Set the task status to pending"
"Notify the assigned user about the new task"
feature ListTasks
description "Lists and filters the requesting user's tasks"
version "1.0.0"
params
status_filter enum["all", "pending", "completed", "archived"]
sort_by enum["created", "priority", "due_date", "title"]
limit integer
actions
"Select tasks owned by the requesting user"
"Filter selected tasks by status_filter"
"Sort selected tasks according to sort_by"
"Limit results to the requested count"
"Return tasks with pagination metadata"
feature UpdateTask
description "Updates editable attributes of a task"
version "1.0.0"
params
task_id string
title string
status enum["pending", "completed", "archived"]
priority enum["low", "medium", "high", "urgent"]
actions
"Look up the task owned by the requesting user"
"Reject the update if the task is archived"
"Validate title is not empty when provided"
"Apply the requested task changes"
"Set the last-modified timestamp"
feature DeleteTask
description "Permanently removes a task after confirmation"
version "1.0.0"
params
task_id string
confirmation string
actions
"Look up the task owned by the requesting user"
"Verify confirmation matches the task title"
"Reject the deletion if the task is archived"
"Remove the task"
"Notify authorized observers about the deletion"
endpoint SaveTask "/api/tasks"
description "Creates a task through the task API"
version "1.0.0"
method "POST"
params
title string
description string
priority enum["low", "medium", "high", "urgent"]
due_date string
assignee_id string
responses
TaskResponse
id integer
title string
description string
priority string
due_date string
assignee_id string
status string
actions
"Validate title is not empty"
"Validate assignee_id identifies an existing user"
"Reject the request when it would create a duplicate task"
"Create the task with a unique identifier"
"Set the task status to pending"
returns
201 TaskResponse
400 "Invalid task data"
403 "The user may not create a task for that assignee"
409 "A duplicate task already exists"
endpoint ListTasks "/api/tasks"
description "Lists tasks through the task API"
version "1.0.0"
method "GET"
params
status_filter enum["all", "pending", "completed", "archived"]
sort_by enum["created", "priority", "due_date", "title"]
limit integer
responses
TaskListResponse
tasks array[string]
total integer
next_cursor string
actions
"Select tasks owned by the requesting user"
"Filter selected tasks by status_filter"
"Sort selected tasks according to sort_by"
"Limit results to the requested count"
"Return tasks with pagination metadata"
returns
200 TaskListResponse
400 "Invalid filter or limit"
Using the output with AI
Compile the spec, then give the compiled prompt to your AI agent together with your implementation context. Review and test the result as you would any other implementation.
Implementation context:
- Use Laravel + Vue
- Follow the existing project structure and conventions
Butter specification:
[paste compiled .prompt.md here]
Implement the application intent. Keep the actions in their declared order
and respect the rules, params, enforce expressions, and endpoint contracts.
What Butter Intentionally Does Not Express
Butter is not a programming, framework, database, deployment, UI design, or visual styling language, and it does not configure technology.
For user interfaces, Butter can express the behavior of an interaction. It does not describe layouts, colors, typography, spacing, component dimensions, or responsive breakpoints. Provide those through design artifacts or implementation context.
Butter can express: “A user must be able to filter their tasks by status.”
Butter does not need to express: “Place the filter dropdown 24px from the right edge and use a 14px font.”
The first statement is application intent. The second is a design or implementation concern.