Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# AGENTS.md

This repository contains the **API Enhancement Proposals (AEPs)** — the design
specification documents for the [AEP project](https://aep.dev). It is a
content-driven repository: the primary artifacts are Markdown/Jinja2 documents
and YAML metadata, not application code.

## Repository layout

```
aep/general/<NNNN>/ # One directory per AEP (zero-padded, 4-digit number)
aep.yaml # AEP metadata (id, state, slug, category, etc.)
aep.md.j2 # AEP content (Markdown + Jinja2 template syntax)
config/ # Site configuration (hero, header, urls, site)
scripts/ # Tooling: fix.py, validate_links.py, build.sh, serve.sh
pages/general/ # Static site pages (adopting, faq, licensing, etc.)
blog/ # Blog posts
.github/workflows/ # CI: lint.yaml, test.yaml, publish_site.yaml
```

## AEP file structure

Every AEP lives in `aep/general/<NNNN>/` and contains exactly two files:

### `aep.yaml` — metadata

Required fields:

- `id`: integer AEP number (no zero-padding)
- `state`: one of `draft`, `reviewing`, `approved`, `final`, `replaced`,
`withdrawn`
- `slug`: URL-friendly short name
- `created`: ISO-8601 date (`yyyy-mm-dd`)
- `placement.category`: one of `meta`, `general`, `resources`,
`standard-methods`, `fields`, `types`, `design-patterns`, `batch-methods`,
`best-practices`, `protobuf`

Optional fields: `updated`, `placement.order`, `redirect_from`, `js_scripts`.

### `aep.md.j2` — content

Markdown with Jinja2 template directives. Key conventions:

- Starts with a single `#` title (a noun, not an imperative).
- Introduction paragraph (no heading) → `## Guidance` section.
- Optional trailing sections in order: `## Rationale`, `## History`,
`## Further reading`, `## Changelog`.
- Only use heading levels `##` and `###` (never `#` beyond the title).
- Use RFC-2119 keywords (`**must**`, `**should**`, `**may**`) in lower-case
bold.
- Use `{% tab proto %}` / `{% tab oas %}` / `{% endtabs %}` for
protocol-specific examples.
- Use `{% sample '<path>', '<selector>' %}` to include code from example files.
- Cross-reference AEPs as `AEP-N` (no zero-padding) in prose, with links using
relative paths like `./0008.md` or `../0008.md`.
- Links must NOT end in `.md` (the linter strips `.md` suffixes). Exception:
external GitHub links.
- Do not use self-reference links like `[aep-123][aep-123]` — use plain text.
- Do not use reference-style links with AEP identifiers like
`[aep-123]: ./0123`.
- Wrap reference-style link definitions that would break prettier in
`<!-- prettier-ignore-start -->` / `<!-- prettier-ignore-end -->` blocks.

## Formatting and linting

Formatting is enforced by CI. Always run before committing:

```bash
make lint
```

This runs three checks:

1. **Prettier** (`npm run check`): Markdown/YAML/JSON formatting.
- `printWidth: 79`, `proseWrap: always`, `singleQuote: true`,
`trailingComma: es5`.
- `.md.j2` files are parsed as Markdown.
2. **`scripts/fix.py`**: Validates and fixes AEP-specific link rules.
- No `.md` suffixes in internal links.
- All AEP cross-references point to existing AEPs.
- No self-reference links, no AEP-identifier reference-style links.
- HTTP URLs are well-formed.
3. **`scripts/validate_links.py`**: Read-only link validation (same rules as
fix.py but never modifies files).

Use `make check` for a read-only lint pass (CI uses this).

## Development

### Prerequisites

- Node.js + npm
- Python 3

### Install dependencies

```bash
make install # runs npm install
```

### Local preview

```bash
./scripts/serve.sh
```

This clones the [site-generator](https://github.com/aep-dev/site-generator) (if
not already present as a sibling directory), builds the site, and starts a dev
server on port 4321.

### Build

```bash
./scripts/build.sh
```

Builds the full site including the site-generator, api-linter, and
aep-openapi-linter (uses sibling directories if present, otherwise clones to
`/tmp`).

## CI workflows

| Workflow | Trigger | What it does |
| ------------------- | ----------- | ------------------------------------------------- |
| `lint.yaml` | PR → main | `make check` (prettier + fix.py + validate_links) |
| `test.yaml` | PR → main | `./scripts/build.sh` (full site build) |
| `publish_site.yaml` | push → main | Triggers site-generator repository dispatch |

## Content conventions

- AEPs should be concise — roughly two printed pages.
- A single AEP covers a single topic.
- API design examples should be presented in both OpenAPI (OAS 3.1) and
protocol buffers.
- Use snake_case for parameter and property names in examples.
- Error codes in prose use the format `{error_code} / {http_status_code}` (e.g.
`OK / 200`).
- Example files (`example.oas.yaml`, `example.proto`) in `aep/general/` are
code-generated by [aepc](https://github.com/aep-dev/aepc) — do not edit them
by hand.

## Common pitfalls

- **Don't link to `.md` files** in AEP content (except external GitHub links).
Links like `./0131.md` will be auto-fixed to `./0131` by `fix.py`, but will
fail `make check`.
- **Don't forget `make lint`** before committing. Prettier reformats prose
wrapping at 79 columns, and fix.py catches link issues.
- **Don't edit `example.oas.yaml` or `example.proto`** — these are generated
from aepc.
- **Zero-pad directory names** but not prose references. Directory: `0008/`.
Prose: `AEP-8`.
2 changes: 1 addition & 1 deletion MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ approval privileges to the aep-dev organization.
| --------------- | ------------------------------------------------ | -------------- |
| Alfred Fuller | [@alfus](https://github.com/alfus) | Buf |
| Alex Stephen | [@rambleraptor](https://github.com/rambleraptor) | Google |
| Dan Hudlow | [@hudlow](https://github.com/hudlow) | IBM |
| Dan Hudlow | [@hudlow](https://github.com/hudlow) | |
| Mike Kistler | [@mkistler](https://github.com/mkistler) | Microsoft |
| Mak Ahmad | [@makahmad](https://github.com/makahmad) | |
| Marsh Gardiner | [@earth2marsh](https://github.com/earth2marsh) | Rubrik |
Expand Down
5 changes: 3 additions & 2 deletions aep/general/0004/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ message UserEvent {
plural: "user-events"
// define one or more patterns, e.g. if a resource has more than one parent.
pattern: "projects/{project_id}/user-events/{user_event_id}"
pattern: "folder/{folder_id}/user-events/{user_event_d}"
pattern: "folder/{folder_id}/user-events/{user_event_id}"
pattern: "users/{user_id}/events/{user_event_id}"
};

Expand Down Expand Up @@ -91,11 +91,12 @@ element = variable | literal;
variable = "{", literal, "}";
```

Where `literal` matches the regex `[a-z][a-z0-9\-_]*[a-z0-9]`.
Where `literal` matches the regex `[a-z](?:[a-z0-9\-_]*[a-z0-9])?`.

- Patterns **must** match the possible [paths](/paths) of the resource.
- Pattern variables (the segments within braces) **must** match the singular of
the resource whose id is being matched by that value, suffixed with `_id`.
- Pattern variables (the segments within braces) **must** use snake case.

#### Pattern uniqueness

Expand Down
3 changes: 2 additions & 1 deletion aep/general/0133/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ Create methods implement a common request message pattern:

{% tab oas %}

{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books.post.responses.200' %}
{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books.post.responses.201' %}

- The response **must** return a `201 Created` HTTP status code.
- The response **must** be the resource itself. There is no separate response
schema.
- The response **should** include the fully-populated resource, and **must**
Expand Down
2 changes: 1 addition & 1 deletion aep/general/0134/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Update methods implement a common request pattern:

{% tab oas %}

{% sample '../example.oas.yaml', '$.paths./publishers.post.responses.200' %}
{% sample '../example.oas.yaml', '$.paths./publishers.post.responses.201' %}

{% endtabs %}

Expand Down
8 changes: 5 additions & 3 deletions aep/general/0136/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ apply consistently:
- The HTTP URI **must** use a `:` character followed by the custom verb
(`:archive` in the above example), and the verb in the URI **must** match the
verb in the name of the RPC.
- If word separation is required, `kebab-case` **must** be used.
- If word separation is required, `kebab-case` **must** be used (e.g.
`:translate-text`, not `:translateText`).
- The name of the RPC **should** be a verb followed by a noun.
- The name of the RPC **must not** contain prepositions ("for", "with",
etc.).
Expand Down Expand Up @@ -106,7 +107,7 @@ permanent effect on data within the API.

{% tab oas %}

{% sample 'translate.oas.yaml', '$.paths./projects/{projectId}:translateText' %}
{% sample 'translate.oas.yaml', '$.paths./projects/{project_id}:translate-text' %}

{% endtabs %}

Expand All @@ -116,7 +117,8 @@ permanent effect on data within the API.
used.
- The URI **should** place both the verb and noun after the `:` separator
(avoid a "faux collection key" in the URI in this case, as there is no
collection). For example, `:translateText` is preferable to `text:translate`.
collection). For example, `:translate-text` is preferable to
`text:translate`.
- Stateless methods **must** use `POST` if they involve billing.

### Usage in declarative clients
Expand Down
4 changes: 2 additions & 2 deletions aep/general/0136/translate.oas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ info:
title: Library
version: 1.0.0
paths:
/projects/{projectId}:translateText:
/projects/{project_id}:translate-text:
post:
operationId: translateText
description: Translates the provided text from one language to another.
Expand Down Expand Up @@ -37,7 +37,7 @@ paths:
application/json:
schema:
description: |
Response structure for the translateText operation.
Response structure for the translate-text operation.
properties:
translated_text:
type: string
Expand Down
2 changes: 1 addition & 1 deletion aep/general/0136/translate.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ service Translate {
// Translates the provided text from one language to another.
rpc TranslateText(TranslateTextRequest) returns (TranslateTextResponse) {
option (google.api.http) = {
post: "/v1/{project=projects/*}:translateText"
post: "/v1/{project=projects/*}:translate-text"
body: "*"
};
}
Expand Down
2 changes: 1 addition & 1 deletion aep/general/0137/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Apply methods implement a common request message pattern:

{% tab oas %}

{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.put.responses.200' %}
{% sample '../example.oas.yaml', '$.paths./publishers/{publisher_id}/books/{book_id}.put.responses' %}

- If the resource is created, the response **must** return a `201` status code.
- If the resource is updated, the response **must** return a `200` status code.
Expand Down
6 changes: 3 additions & 3 deletions aep/general/0231/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ APIs **may** support batch get to retrieve a consistent set of resources.
- The method's name **must** begin with `BatchGet`. The remainder of the method
name **must** be the plural form of the resource being retrieved.
- The HTTP verb **must** be `GET`.
- The HTTP URI **must** end with `:batchGet`.
- The HTTP URI **must** end with `:batch-get`.
- The URI path **must** represent the collection for the resource, matching the
collection used for simple CRUD operations. If the operation spans parents, a
[wilcard](./reading-across-collections) **may** be accepted.
Expand Down Expand Up @@ -45,7 +45,7 @@ pattern:
```proto
rpc BatchGetBooks(BatchGetBooksRequest) returns (BatchGetBooksResponse) {
option (google.api.http) = {
get: "/v1/{parent=publishers/*}/books:batchGet"
get: "/v1/{parent=publishers/*}/books:batch-get"
};
}

Expand Down Expand Up @@ -121,7 +121,7 @@ message BatchGetBooksResponse {

{% tab oas %}

{% sample 'batchget.oas.yaml', '$.paths./publishers/{publisherId}/books:BatchGet' %}
{% sample 'batchget.oas.yaml', '$.paths./publishers/{publisherId}/books:batch-get' %}

Example response body:

Expand Down
2 changes: 1 addition & 1 deletion aep/general/0231/batchget.oas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ info:
title: Library
version: 1.0.0
paths:
/publishers/{publisherId}/books:BatchGet:
/publishers/{publisherId}/books:batch-get:
parameters:
- name: publisherId
in: path
Expand Down
24 changes: 18 additions & 6 deletions aep/general/example.oas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ paths:
$ref: '#/components/schemas/isbn'
required: true
responses:
'200':
'201':
Comment thread
toumorokoshi marked this conversation as resolved.
content:
application/json:
schema:
Expand Down Expand Up @@ -269,7 +269,7 @@ paths:
$ref: '#/components/schemas/publisher'
required: true
responses:
'200':
'201':
content:
application/json:
schema:
Expand Down Expand Up @@ -354,7 +354,13 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/publisher'
description: Successful response
description: Successful response (updated)
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/publisher'
description: Successful response (created)
/publishers/{publisher_id}/books:
get:
description: List method for book
Expand Down Expand Up @@ -411,7 +417,7 @@ paths:
$ref: '#/components/schemas/book'
required: true
responses:
'200':
'201':
content:
application/json:
schema:
Expand Down Expand Up @@ -516,7 +522,13 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/book'
description: Successful response
description: Successful response (updated)
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/book'
description: Successful response (created)
/publishers/{publisher_id}/books/{book_id}/editions:
get:
description: List method for book-edition
Expand Down Expand Up @@ -579,7 +591,7 @@ paths:
$ref: '#/components/schemas/book-edition'
required: true
responses:
'200':
'201':
content:
application/json:
schema:
Expand Down
Loading
Loading