Butter logo

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: intentImplementation context: technology
Business rules, constraints, and invariantsProgramming language and framework
Validation requirementsDatabase technology and libraries
Workflows and ordered actionsInfrastructure and deployment environment
Expected outcomesArchitecture choices
HTTP endpoint contractsCoding 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 feature and endpoint blocks at depth 0 after the app block.
  • Blank lines: required above every feature and endpoint (except the first block in the file) and above the rules block. 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 expressUse
What the application does; workflowsfeature with ordered actions
Business rules and invariants that apply everywhererules under app
A constraint on one stepenforce under an action
Valid input and validationparams, plus actions or enforce that reject invalid input
What must not happen (boundaries)rules, actions, or enforce, written as prohibitions or rejections
HTTP interfacesendpoint with params, responses, actions, and returns
Expected outcomesactions 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 rules under app, after description and version.
  • 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, and actions.
  • 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

TypeExampleNotes
stringname stringDefault when type is omitted.
integercount integerWhole numbers.
doubleamount doubleFloating-point values.
booleanactive booleantrue 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.
  • method is required and must be one of GET, POST, PUT, DELETE, or PATCH.
  • Valid keywords inside an endpoint are description, version, method, params, responses, actions, and returns.
Feature vs Endpoint: Use feature for application behavior. Use endpoint for an HTTP interface contract. They sit side by side under app.

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

SyntaxMeaning
201 OrderSuccessPayloadStatus 201, body matches the OrderSuccessPayload schema from responses.
400 "Invalid order data"Status 400, body is this string.
404Status 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 app per spec.
  • Unique feature names, endpoint names, param names, and enum values.
  • Valid param types and HTTP methods.
  • Status codes are integers from 100 to 599.
  • Every response name used in returns is defined in the same endpoint's responses.

Fields per block

Only "Required" fields are enforced. "Recommended" and "Optional" are convention and intent; the compiler does not require them.

BlockRequiredRecommendedOptional
appdescriptionversion, rules, feature (zero or more), endpoint (zero or more)
featuredescription, actions (at least 1)version, params
endpointRoute in header, methoddescription, 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.