add paginated section index pages
The first collection page earns the Query primitive: content.Query{Section, Lang}
with Site.Run, newest first, undated after dated, ties broken by key so the same
query always answers in the same order. No cache signature — nothing caches, and a
signature with no consumer is speculation.
Pagination lives in the path (ADR-0028): page one is the bare listing URL,
/page/1/ redirects to it, and a page past the end is 404 rather than an empty page,
because an empty page is a URL that means nothing. `page` is therefore a reserved
segment inside a section, now recorded in content-model.md.
Two kinds of page means two parsed template sets already — base plus the block that
kind defines — which is ADR-0019's per-type shape arriving by need rather than by
anticipation. A head struct is embedded in both Page and List so base.html has one
contract, and theme-contract.md gains the listing fields.
Bundle gains Date, accepting an unquoted YAML date or an RFC 3339 string, since
yaml.v3 hands back time.Time for one and a string for the other.
Evidence: 12 posts → /posts/ shows 10 with rel=next to /posts/page/2/,
/posts/page/2/ shows 3 with rel=prev to /posts/, ordering is post-12 11 10,
/posts/page/1/ 301s to /posts/, /posts/page/9/ is 404, /bn/posts/ is 200.
This commit is contained in:
@@ -70,7 +70,7 @@ readable by templates (ADR-0002). Never add a required field.
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `title` | string | Only required field |
|
||||
| `date` / `updated` | date | Publication; `updated` drives feeds and `Last-Modified` |
|
||||
| `date` / `updated` | date | Publication; `updated` drives feeds and `Last-Modified`. An unquoted `2026-07-30` or an RFC 3339 timestamp; undated bundles sort after dated ones |
|
||||
| `type` | string | Post type; defaults from the top-level section |
|
||||
| `slug` | string | Overrides the derived slug. The engine serves the new path only; the old one 404s unless it appears in `aliases` |
|
||||
| `aliases` | []string | Paths the engine redirects permanently to this bundle's canonical URL (ADR-0008). A scalar or a list; surrounding slashes optional. An alias naming a real bundle, or claimed by two bundles, is ambiguous — logged and dropped, and the real bundle keeps its URL |
|
||||
@@ -128,6 +128,9 @@ Overrides are normalised like everything else: writing a slug by hand does not e
|
||||
|
||||
## Permalinks
|
||||
|
||||
Listings paginate at `/{section}/page/N/` (ADR-0028), so `page` is a reserved segment inside a section:
|
||||
no bundle may be slugged `page`. Page one is the bare listing URL and `/page/1/` redirects to it.
|
||||
|
||||
`/{section}/{slug}/`, no exceptions (ADR-0008). Section is the content type — the top-level directory
|
||||
under `content/`, including `pages` — and slug comes from the bundle path or a `slug` override. So
|
||||
`pages/about/` serves at `/pages/about/`, and the root stays engine-owned: emitted files and future
|
||||
|
||||
Reference in New Issue
Block a user