resolve sequences from the directory tree

A bundle nested under another bundle is a member of that series (ADR-0033), so
`Site.Sequence` walks up to the nearest bundle ancestor and back down to its
members: ordered by `order` where set, then by name. Members resolve through the
language fallback, so a chapter with no Bengali variant still holds its place in
Bengali reading order instead of breaking prev/next.

One `.Sequence` field carries both shapes a theme needs. A landing page renders
`.Members` as an archive; a chapter renders `.Prev`/`.Next`, which are pointers
into `.Members` so `{{with}}` yields nothing at the ends. `Index == 0` is what
tells the two apart.

`Query` was deliberately not extended. A series ascends where `Run` descends, and
an order knob on `Query` is the config knob rule 6 bans; instead `Site.keys()`
came out so both iterate the index one way, deleting `Run`'s own dedupe map.

`draft` is not honoured: no bundle carries the field and nothing else excludes
drafts, so entry 19 adds it in both places at once. Recorded in content-model.md
rather than left implied.

state.md also corrects six inventory rows that had drifted before this change —
three LOC figures, the test total, `go.mod`, and two lines that were flatly wrong
("Dependencies: none", "goldmark is not yet imported"). The coupling gate proves
state.md changed with the code; it cannot prove the numbers are right.
This commit is contained in:
2026-08-01 02:23:35 +06:00
parent 50f3bd1942
commit 7789242185
10 changed files with 427 additions and 41 deletions
+15
View File
@@ -21,6 +21,21 @@ A bundle page receives:
| `.Style` | the reference theme's stylesheet, inlined so a bare site root needs no asset route |
| `.Canonical` | the permalink of the variant actually served — not the URL requested, which differs when the fallback chain supplied another language |
| `.Alternates` | every language this key exists in, as `.Lang` and `.URL`, for `hreflang` |
| `.Sequence` | the series this page sits in, absent when it sits in none (ADR-0033) |
`.Sequence` carries the reading order and this page's place in it:
| Field | Contents |
|---|---|
| `.Sequence.Title`, `.Sequence.URL` | the series' title (may be empty) and its permalink |
| `.Sequence.Members` | every entry in reading order, each as an `.Items` entry — *ascending*, unlike a dated listing |
| `.Sequence.Index`, `.Sequence.Count` | this page's 1-based position and the total; `Index` is 0 when this page is the series landing itself |
| `.Sequence.Prev`, `.Sequence.Next` | the neighbours, absent at the ends and on a landing page. `Prev` is the **earlier** entry — the opposite sense of a listing's `.PrevURL` |
| `.Sequence.First`, `.Sequence.Last` | the ends of the series, present whenever it has members |
A landing page therefore renders an archive from `.Members` and a chapter renders navigation from
`.Prev`/`.Next`, both from one field. Membership and order are the engine's business
(`content-model.md`); a theme never sorts.
Two named templates: `base` is executed for every page; `main` is the block each kind of page defines and
a theme redefines. There is one parsed set per kind — bundle and listing today — so two kinds may both