---
name: spec
version: 1
lock: sha256:1f89a1d9eb90390c7365cd0f07520bb3b44df523e34dda77048ee590ef193d37
---

# Goal
Someone who has never seen an .intent file can read, write and verify one using only this page.

# Evals
- test: every example on this page parses → intent examples "$INTENT_FILE"
- judge: a developer could write a working parser from this page alone, without guessing
- review: it describes what I mean by .intent

# Fulfillment
## The shape of a file

An .intent file is plain UTF-8 text. It has front matter, then three required sections and one optional one, always in this order:

| Section | What it holds | Locked within a version? |
| --- | --- | --- |
| `# Goal` | one plain sentence stating the expected outcome | yes |
| `# Evals` | the checks that say whether the work meets the goal | yes |
| `# Fulfillment` | the work itself: text, design, spec or code | no |
| `# Results` | recorded verdicts for evals (optional) | no |

Section headings use a single `#`. Inside Fulfillment, use `##` and below. A `#` line inside a code block is not a heading.

To read the sections exactly:

- Treat `\r\n` and `\r` as line breaks, the same as `\n`.
- A section heading is a line that starts with `# ` followed by the section name. The name ignores case (`# goal` is `# Goal`), and trailing `#` characters are dropped.
- Any other `# ` heading is an error, and so is a section that appears twice, sections out of order, or any non-blank text between the front matter and `# Goal`.
- A section holds every line after its heading until the next section heading or the end of the file.
- Code blocks are fenced with three or more backticks or tildes, indented by at most three spaces. A fence closes at a line with the same character, at least as many of them, and nothing else after them. Lines inside a fence are never headings.

## Front matter

The file starts with `---`, a few `key: value` lines, and a closing `---`. The opening line may have spaces around it, but the closing line must be exactly `---`.

- A key starts with a letter and continues with letters, digits, `_` or `-`. The colon follows the key directly; the value is everything after it, trimmed.
- If the value is wrapped in one pair of matching quotes, `"..."` or `'...'`, the quotes are removed: `title: "Who checks the checker?"` has the title `Who checks the checker?`. Nothing else is unescaped.
- Blank lines and lines starting with `#` are ignored. Any other line that isn't `key: value` is an error.

The keys an .intent file knows:

- `name`, required: lowercase letters, digits, `.`, `_` or `-`, starting with a letter or digit.
- `version`, required: a whole number, 1 or higher.
- `supersedes`, optional: the version this one replaces, written `name@version`.
- `lock`, required to verify: the hash of the goal and evals, written `sha256:<hex>`. See [The lock](#the-lock).
- `title`, optional: a headline for readers. The goal is written to be checked; the title is written to be read. The title is not locked, so changing it never needs a new version.

Other keys are allowed, and tools ignore keys they don't know.

## Goal

One plain sentence. No lists, no second paragraph. The goal says what the work is *for*, not what it contains.

The goal is the section's text, trimmed. It must not be empty or contain a blank line. A sentence that wraps over several lines is joined with single spaces.

## Evals

Each eval is a list item that starts with its kind:

| Kind | Who runs it | Blocks when it fails? |
| --- | --- | --- |
| `test:` | any computer, as a shell command | yes |
| `judge:` | an AI-assisted computer, as a plain-sentence instruction | yes |
| `review:` | a person | yes |
| `watch:` | measured from the outside world, such as traffic or adoption | never |

A `judge:` eval never names an AI provider or model. Whatever assistant the computer has is the judge.

Every eval has a status: **✓** pass, **✗** fail, or **○** pending. Only ✗ blocks. A file can be partly verified: an ordinary computer runs the tests and leaves the judges ○, and that is a valid state, not an error.

How the Evals section is laid out:

- An eval starts with `- ` at the very start of a line, then the kind, a colon, and its text: `- judge: the reminder makes sense in a single line`. Any other kind is an error.
- An indented line belongs to the eval above it. Without a code block, indented lines are joined onto the text with single spaces, so a long sentence can wrap.
- Blank lines are allowed. Any other line that isn't an eval or indented under one is an error.
- The section needs at least one eval. Evals are numbered from 1 within each kind, in order: the second `judge:` is judge 2.
- `judge:` and `review:` evals are plain sentences. An arrow in them is just text, and a code block under one is an error.

## Writing tests

A test is a shell command that passes when it exits with 0. It runs in the folder that holds the file, with the file's path in the `INTENT_FILE` environment variable. Three forms are valid:

- **One line.** The text is the command: `- test: npm run build`
- **Named.** A readable name, an arrow, then the command: `- test: the site builds → npm run build`. Readers see the name. `->` works too. The arrow needs whitespace on both sides, and the text splits at the first one, so later arrows belong to the command.
- **Fenced.** A name, with a fenced code block indented underneath it that holds the script. The indented lines are dedented first. Any text lines before the fence are joined onto the name; the lines between the fences are the command.

A `watch:` can use the named or fenced form to measure something; without a command it is a description waiting to be measured.

## Fulfillment

The actual work, written in Markdown. It is free to change at any time, as long as the evals still pass.

## Results

Recorded verdicts live at the bottom of the file, one per line, matched to evals by kind and position:

````text
- judge 1: ✓ 2026-11-12 — the PDF/Markdown contrast lands in the first paragraph
- review 1: ○ pending
````

Each result line has this shape:

````text
- <kind> <number>: <mark> <date> — <reason>
````

- `<kind> <number>` names the eval, numbered the same way as in Evals.
- `<mark>` is `✓`, `✗` or `○`.
- `<date>` is optional and written `YYYY-MM-DD`. The separator after it is optional too: an em dash, an en dash, a hyphen or a colon.
- The reason is the rest of the line. Indented lines underneath it are joined onto it with single spaces.
- Other lines in Results that aren't `- ` items are notes, and tools ignore them.

An eval with no result line, or with `○`, is pending. Tests are not usually recorded here, because they run every time. Recording a verdict never needs a new version.

## The contract

Within a version, the goal and evals never change. To change either one, write a new version: raise `version` by one and set `supersedes` to the old `name@version`. The old version stays readable, so the history shows how the goal evolved, and old evals can be run again on new work to catch regressions.

An agent running the fulfillment treats the goal and evals as read-only. It may change the work in any way, but never the checks it is judged by; otherwise the easiest way to reach the goal would be to weaken the test. A tool that hands a file to an agent should check the lock before and after the run.

## The lock

The `lock` makes the contract checkable from the file alone, without any history. To compute it:

1. Take the text of the Goal section and the text of the Evals section.
2. In each, trim the ends and collapse every run of whitespace to a single space.
3. Join them with one newline: goal first, then evals.
4. Hash the result as UTF-8 with SHA-256 and write it as `sha256:` followed by 64 lowercase hex characters.

If the recomputed hash differs from `lock`, the goal or evals were changed after locking, and the file fails verification. The reference tool writes the lock with `intent lock`, and `intent lock --bump` starts a new version when the goal really has changed.

When an eval fails, the work no longer meets its goal. There are two ways forward: change the fulfillment until it passes, or write a new version with a new goal.

## Not in this spec

- How a tool treats commands in a file from someone it doesn't trust.
- Which AI assistant runs judges.
- How a file looks when rendered.

## A complete example

````intent
---
name: onboarding
version: 2
supersedes: onboarding@1
lock: sha256:e19373c73488c154cf44a73833abf01db05d3d98a0e21c0fd7f416dbfca166ad
---

# Goal
New users book their first match within 5 minutes of signup.

# Evals
- test: signup to booking takes under 5 minutes → ./checks/median-signup-to-booking.sh 300
- test: every screen has a back button
  ```sh
  ./checks/screens.sh | grep -v 'back: yes' && exit 1 || exit 0
  ```
- judge: the flow feels effortless for a first-time player
- review: support agrees the copy answers the usual first questions
- watch: weekly share of new users who book within a day

# Fulfillment
## Steps
1. Pick a sport.
2. Pick a nearby club.
3. Pick a time and book.

# Results
- judge 1: ✓ 2026-11-12 — three short steps, no dead ends
- review 1: ○ pending
````

# Results
- judge 1: ✓ 2026-11-10 — the page now gives the exact rules for line breaks, headings, fences, front-matter keys and quotes, how eval lines wrap and split, and the Results line grammar. A parser built from it handles every file on this site the same way the reference tool does
