correct five claims about behaviour this engine does not have
All five were found by checking content-model.md against the parser rather than by any gate, which is the point: verify.sh catches a dangling path and a missing ADR number, never a sentence that is merely untrue. The Scaffolding section documented `khosra demo <empty dir>` writing a generated site. ADR-0051 deleted that generator eleven commits ago in favour of the tracked site in examples/, so the paragraph described a subcommand main.go never dispatched — and justified itself with "nothing in the engine repository is content", which the committed demo site contradicts. Deleted rather than rewritten: what the demo is belongs to state.md's inventory, and restating it here would have broken the single-source rule to fix a smaller problem. ADR-0050 is where that claim originated and it still read "Status: accepted", so a reader arriving there had no way to learn the generator was gone. Its Status line now names ADR-0051 as superseding that half. This deviates from decisions.md being append-only, so the exception is recorded in the mutability column where the convention lives: a Status line may be annotated in place, the Decision text never. "The parser does not read `slug` yet" was false and contradicted by the frontmatter table twelve lines above it in the same file. Deleted. The Sequences section and the Sequence doc comment both said `draft` is not honoured "because no bundle carries it yet". Two errors: the demo site carries one, and draft is honoured — membership resolves through Lookup, which is the single place ADR-0024 hides unpublished bundles. The doc now says what actually happens, including that prev/next closes over the gap. state.md said draft "lands in Extra unread". It is lifted to Bundle.Draft and deleted from Extra, and content_test.go already asserted it. Evidence: a three-chapter series with a draft middle, served by a freshly built binary. The landing lists First Rain and Third Rain; chapter one's next points at three/, not two/; the draft's own URL is 404. That absence has no test, so it is now a latent item with the reason it is safe by construction. 6 files, +25/-25. No counter moves. surface.md regenerated: the comment gained a line and shifted ten declaration numbers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ Read the one you need. Do not read them all.
|
||||
|---|---|---|
|
||||
| `architecture.md` | The six primitives, invariants, per-primitive STATUS | Only via an ADR |
|
||||
| `state.md` | What exists now: inventory, earn-it counters, latent items | Every feature |
|
||||
| `decisions.md` | ADR log — one entry per load-bearing choice | Append-only |
|
||||
| `decisions.md` | ADR log — one entry per load-bearing choice | Append-only. One exception: a superseded entry's **Status** line is annotated in place to name what replaced it, because a reader arriving at the older entry has no other way to learn it was replaced. Never the Decision text — that stands as history |
|
||||
| `roadmap.md` | Arcs, earn-triggers, the core freeze point | When an arc completes |
|
||||
| `content-model.md` | On-disk layout, frontmatter, post types, permalinks — **what the parser accepts today**, never a plan | When the disk format changes |
|
||||
| `conventions.md` | Go style floor, package layout, perf and test rules | Rarely; via ADR if contested |
|
||||
|
||||
@@ -164,7 +164,7 @@ numbers stay ASCII digits, and a slug is whatever the key says: `/bn/lekha/page/
|
||||
spelling of it. Localisation is for the words around the content, never the address of it (ADR-0034). A
|
||||
Bengali variant is therefore reached at the same key under a prefix — `/bn/posts/hello-world/`. A `slug`
|
||||
override renames the bundle in every language at once (ADR-0035); nothing gives one variant an address of
|
||||
its own. The parser does not read `slug` yet.
|
||||
its own.
|
||||
|
||||
A language prefix wins over a section of the same name, so a site with Bengali content may not also have
|
||||
a section called `bn`. The engine treats a leading segment as a language only when some bundle is written
|
||||
@@ -197,17 +197,13 @@ language fallback chain, so a chapter missing in Bengali still appears in Bengal
|
||||
than breaking prev/next. Reading order is *ascending*, the opposite of a dated listing: a sequence's
|
||||
"prev" is the earlier entry.
|
||||
|
||||
`draft` is not honoured yet, because no bundle carries it and nothing else excludes drafts either; the
|
||||
feature that adds the field adds it here in the same change.
|
||||
A draft or future-dated member is not in the sequence at all: membership resolves through the same
|
||||
`Lookup` every other path to a bundle uses, and that is where unpublished bundles are hidden (ADR-0024) —
|
||||
one rule in one place rather than a second check here. So prev/next closes over the gap, and `-dev on`
|
||||
reveals the member in its proper position without changing anything else.
|
||||
|
||||
## Scaffolding
|
||||
|
||||
`khosra demo <empty dir>` writes a whole site that exercises every feature — both languages with a fallback, a
|
||||
series with ordered chapters, a gallery, a figure, an include, extras, tags across sections, a slug with an
|
||||
alias, an undated page, a draft, a template override, `static/` and `site.yaml`. Its prose is generated filler,
|
||||
not stored content: nothing in the engine repository is content (ADR-0011), and a demo that cannot express a
|
||||
feature is evidence of a gap in the engine.
|
||||
|
||||
`khosra new posts/hello-world` writes `content/posts/hello-world/index.en.md` — a **directory** bundle, because
|
||||
only that shape can own local files, so scaffolding the other kind would hand an author a page their pictures
|
||||
cannot live beside. `-lang` picks the language suffix and `-title` the title, which otherwise comes from the
|
||||
|
||||
+3
-1
@@ -734,7 +734,9 @@ Revisit if: a fourth set-once callback appears. Three would say the renderer wan
|
||||
struct rather than a constructor plus setters.
|
||||
|
||||
## ADR-0050 — The root lists everything; `khosra demo` writes a site, not content in this repo
|
||||
Date: 2026-07-31 · Status: accepted
|
||||
Date: 2026-07-31 · Status: accepted for the root listing; the `khosra demo` half is superseded by ADR-0051,
|
||||
which deleted the generator in favour of the tracked site in `examples/`. The Decision text below stands as
|
||||
written — read it as history, and never as a subcommand that exists.
|
||||
Decision: `/` serves a listing of every bundle, newest first, paginated like any other — and 404s only when
|
||||
nothing is published. And `khosra demo` writes a site root that exercises every feature, generating its filler
|
||||
rather than copying stored files, into an empty directory the human names.
|
||||
|
||||
+3
-2
@@ -60,8 +60,8 @@ polls four times a second (ADR-0056).
|
||||
`-site` (ADR-0011). `site.yaml` declares `base` and `title`; with a base, canonical, hreflang and OpenGraph
|
||||
URLs go absolute (ADR-0039).
|
||||
|
||||
Frontmatter the parser lifts today: `title`, `date`, `tags`, `aliases`, `order`, `slug`. `include: embed` is read from `Extra` by the renderer; merging is the default (ADR-0066, ADR-0076). Every other key in
|
||||
`content-model.md`'s table — including `draft` and `type` — lands in `Extra` unread, so that table
|
||||
Frontmatter the parser lifts today: `title`, `date`, `tags`, `aliases`, `order`, `slug`, `draft`. `include: embed` is read from `Extra` by the renderer; merging is the default (ADR-0066, ADR-0076). Every other key in
|
||||
`content-model.md`'s table — including `type` — lands in `Extra` unread, so that table
|
||||
is the accepted format, not a list of what runs.
|
||||
|
||||
`examples/demo-site/` is a complete site kept in the repository to be read and served — 30 bundles across six
|
||||
@@ -123,6 +123,7 @@ with a stated reason. A list nothing drains is a graveyard of known defects.
|
||||
| A gallery's images carry no `alt` | `width`/`height` now come from the original (ADR-0042), so only alt text is missing, and a filename does not supply one. An empty `alt` is honest for a picture the page has already introduced | Captions per gallery entry — a sidecar or a frontmatter list — if the reference theme ever needs them |
|
||||
| Sequence resolution rescans the index on every bundle request — two passes over every key, each doing a `Lookup` | Measured at the same time as the pictures (ADR-0044): a whole page is ~63µs, so this is not what costs anything. Remembering it would be a cache with no measurement behind it | A page render exceeding a few milliseconds, which is also what would revive the parked cache model |
|
||||
| The root listing's `<title>` repeats itself — "A Khosra Demo · A Khosra Demo" | Spotted 2026-08-01 by looking at the served page, not by any test: `base.html` joins page title and site title unconditionally, and at the root those are the same string. Cosmetic, and the fix is one `if` in a template — theme layer, not engine | The next time `base.html` is edited for any reason — its old trigger named queue entry G4, which has been dropped |
|
||||
| A draft member's absence from a sequence has no test | Proven by hand against the real binary on 2026-08-02 — a three-chapter series with a draft middle lists two members, prev/next closes over the gap, and the draft 404s. It holds by construction: `members()` resolves through `Lookup`, which is the one place ADR-0024 hides unpublished bundles, so there is no second code path to drift | The first change to `members()`, or to how `Lookup` decides visibility |
|
||||
| Under `include: embed`, a fragment's footnotes render where the include sits, so a long one puts an `<hr>` and a numbered list mid-article | Spotted 2026-08-01 by looking at the served page, not by any test. It is ADR-0038's documented consequence, and the ids are correctly namespaced (ADR-0058); only the placement reads badly. Merging is the default since ADR-0076, so this is now something an author opts into by asking for containment | Nothing: it is the documented cost of the model you chose |
|
||||
|
||||
## Open questions
|
||||
|
||||
+10
-10
@@ -22,9 +22,9 @@ check.go 45 · main.go 177 · new.go 42 · wire.go 41
|
||||
- wire.go:18 func theme(siteFS fs.FS, settings content.Settings) (*render.Renderer, error)
|
||||
- wire.go:31 func extenders(partial render.Partial) []goldmark.Extender
|
||||
|
||||
## internal/content — 1043 lines + 558 test
|
||||
## internal/content — 1044 lines + 558 test
|
||||
|
||||
clock.go 12 · content.go 451 · doc.go 5 · extras.go 92 · settings.go 59 · site.go 424
|
||||
clock.go 12 · content.go 451 · doc.go 5 · extras.go 92 · settings.go 59 · site.go 425
|
||||
|
||||
- clock.go:9 var now = time.Now
|
||||
- clock.go:12 func Now() time.Time { return now() }
|
||||
@@ -86,14 +86,14 @@ clock.go 12 · content.go 451 · doc.go 5 · extras.go 92 · settings.go 59 · s
|
||||
- site.go:243 func (s *Site) Run(q Query) []Bundle
|
||||
- site.go:268 func (s *Site) keys() []string
|
||||
- site.go:284 type Sequence struct
|
||||
- site.go:304 func (s *Site) Sequence(key, lang string) (Sequence, bool)
|
||||
- site.go:325 func (s *Site) parent(key string) (string, bool)
|
||||
- site.go:335 func (s *Site) has(key string) bool
|
||||
- site.go:344 func (s *Site) members(series, lang string) []Bundle
|
||||
- site.go:369 func (b Bundle) hasTag(slug string) bool
|
||||
- site.go:382 func (b Bundle) Section() string
|
||||
- site.go:394 func (s *Site) Everything() []Bundle
|
||||
- site.go:407 func (s *Site) Sections() []string
|
||||
- site.go:305 func (s *Site) Sequence(key, lang string) (Sequence, bool)
|
||||
- site.go:326 func (s *Site) parent(key string) (string, bool)
|
||||
- site.go:336 func (s *Site) has(key string) bool
|
||||
- site.go:345 func (s *Site) members(series, lang string) []Bundle
|
||||
- site.go:370 func (b Bundle) hasTag(slug string) bool
|
||||
- site.go:383 func (b Bundle) Section() string
|
||||
- site.go:395 func (s *Site) Everything() []Bundle
|
||||
- site.go:408 func (s *Site) Sections() []string
|
||||
|
||||
## internal/ext/check — 231 lines + 168 test
|
||||
|
||||
|
||||
@@ -299,8 +299,9 @@ type Sequence struct {
|
||||
// page is about.
|
||||
//
|
||||
// Members resolve through the language fallback chain, so a chapter missing in this language still holds
|
||||
// its place in the reading order rather than breaking prev/next (ADR-0009). `draft` is not honoured
|
||||
// because no bundle carries it yet (content-model.md).
|
||||
// its place in the reading order rather than breaking prev/next (ADR-0009). Membership goes through Lookup,
|
||||
// which is where unpublished bundles are hidden (ADR-0024), so a draft member is absent rather than
|
||||
// filtered here — one rule in one place.
|
||||
func (s *Site) Sequence(key, lang string) (Sequence, bool) {
|
||||
if members := s.members(key, lang); len(members) > 0 {
|
||||
// key names a bundle: members are the bundles nested under it.
|
||||
|
||||
Reference in New Issue
Block a user