ai-manifest/1
A plain-Markdown format for recording how AI tools were, or weren't, used in a project. Like a shipping manifest, it lists what's inside without judging it. A project with no AI involvement and one built almost entirely by AI are described the same way.
Each fact is written once, as text that reads naturally on its own. A small set of conventions makes the same text machine-parseable.
The file is called AI-MANIFEST.md and lives in the repository root.
The canonical home of this spec is https://ai-manifest.ahnlak.com/spec/1.
Examples
A project with no AI involvement:
# AI manifest
- Format: ai-manifest/1
- Updated: 2026-10-02
- Default level: none
No AI tools were used in this project.
A project with mixed involvement:
# AI manifest
- Format: [ai-manifest/1](https://ai-manifest.ahnlak.com/spec/1)
- Updated: 2026-10-02
- Tools: Claude (hosted)
- Default level: none
Designed and written by me. Anything not listed below had no AI involvement.
## Styling
- Level: directed
- Review: full
- Paths: `assets/css/**`
Most of the CSS was generated to my direction, then checked in a browser.
## Code: Rust
- Level: suggested
- Review: full
- Familiarity: expert
All Rust code is mine. Claude suggested some alternative structures.
Structure
- Title. The file starts with exactly one
#heading. Its text is free. - Header. The first list under the title holds the header fields. Any prose after that list, up to the first
##heading, is the summary. - Area sections. Each
##heading starts an area. Its first list holds that area's fields, and any prose after the list is the area's notes. - End marker. An optional horizontal rule (
---,***or___, on its own line, after a blank line) ends the parsed content. Anything after it, such as a legend, is ignored by tools.
Field lists are written as - Key: value. Keys are case-insensitive, and - **Key:** value is also accepted. The field list must come directly after its heading (blank lines are allowed). Any later lists are treated as prose.
### and deeper headings, code blocks and other Markdown are allowed inside prose and are not interpreted.
HTML comments (<!-- ... -->) are ignored outside code blocks, because Markdown hides them from readers. Tools must not read anything a reader can't see.
Whitespace follows ordinary Markdown. Blank lines may appear anywhere, including between field-list items. Headings and list items may be indented by up to three spaces. Spacing around the : is ignored, and so are trailing whitespace, CRLF line endings and a UTF-8 byte-order mark. The one blank line that matters is the one between a field list and the prose after it. Without it, Markdown displays the prose as part of the last field, so the validator warns about it.
Header fields
| Field | Required | Value |
|---|---|---|
Format | yes | ai-manifest/1, optionally as a link (see below) |
Updated | recommended | ISO date, YYYY-MM-DD |
Tools | if AI used | Comma-separated Name (hosting), where hosting is hosted or local. Or none. |
Default level | yes | The level for anything not covered by an area section |
Default review | see below | The review for anything not covered by an area section |
The Format line is what identifies the file as an AI manifest. Tools must ignore a file without it, whatever the file is called. The identifier can be written as a Markdown link to this spec, so human readers can follow it:
- Format: [ai-manifest/1](https://ai-manifest.ahnlak.com/spec/1)
Tools match on the link text and report the link target as spec.
Area sections
The heading is ## Area or ## Area: label. Area names are case-insensitive. The standard areas are:
concept, spec, planning, design, code, debugging, tests, styling, docs, assets, ops
Custom areas are allowed. The validator warns about them, because other tools may not recognise them. The label is free text that distinguishes sections with the same area. For code, the label names the language, as in ## Code: Kotlin. The same area and label combination may appear only once.
| Field | Required | Value |
|---|---|---|
Level | yes | See Levels below |
Review | see below | none, light or full |
Familiarity | optional | expert, proficient or learning. Recommended for code. |
Paths | optional | Backtick-quoted globs, comma-separated: `src/ui/**`, `*.css` |
Levels
These describe who produced the work, from least to most AI involvement.
| Level | Meaning |
|---|---|
none | No AI involvement. |
consulted | Discussed with AI or had things explained. No AI output in the work. |
suggested | AI proposed approaches or snippets. A human wrote the actual work. |
co-written | Substantial AI-written parts, reworked and integrated by a human. |
directed | AI wrote most of it, following detailed human instructions. |
delegated | AI wrote it from a brief. The human set goals, not details. |
Review
Review describes how carefully a human checked AI output. It is required at suggested and above, and has no meaning at none or consulted.
| Review | Meaning |
|---|---|
none | Not reviewed. |
light | Skimmed or spot-checked. |
full | Every part read and understood, and tested where applicable. |
Familiarity
Familiarity is the author's own skill in the language or domain. It shows how well the author could judge what the AI produced.
Extensions
Field names starting with X- (such as - X-Model: ...) are allowed anywhere and are passed through untouched. Any other unknown field is an error.
Validation and JSON
ai-manifest-check.py validates files against this spec. With --json, it prints each parsed file as a JSON list like this:
[{
"file": "AI-MANIFEST.md",
"format": "ai-manifest/1",
"spec": "https://ai-manifest.ahnlak.com/spec/1",
"title": "AI manifest",
"updated": "2026-10-02",
"summary": "Designed and written by me. ...",
"tools": [{"name": "Claude", "hosting": "hosted"}],
"default": {"level": "none", "level_rank": 0, "review": null},
"areas": [{
"area": "code", "label": "Rust", "line": 18,
"level": "suggested", "level_rank": 2,
"review": "full", "familiarity": "expert",
"paths": [], "notes": "All Rust code is mine. ...",
"extensions": {}
}],
"extensions": {}
}]
level_rank counts from 0 (none) to 5 (delegated), so tools can compare levels without hard-coding the list.