Commit Graph
2 Commits
Author SHA1 Message Date
bdeshi 771a491188 add khosra demo, and give the site a front page
The demo writes a whole site root that exercises every feature: two 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 files and site.yaml. It generates its filler rather than
copying stored files, because nothing in this repository is content (ADR-0011) — and
that makes it a test of the engine rather than a fixture: anything khosra can do
that the demo cannot express is a gap.

Two things found by generating and then serving it, which is the whole point:

`khosra check` reported the demo's own series as mixing ordered and unordered
members. It was right — the chapter bodies *described* `order: 10` while the
frontmatter never carried it. The checker caught its own author.

And `/` was a **404**. ADR-0008 leaves the root engine-owned, which is right, but
"engine-owned" was never given an answer, so a visitor to the site's own address got
nothing. The root now lists every bundle, newest first, paginated like any other
listing, and 404s only when nothing is published. A hand-written home page stays a
separate decision, recorded as such.

Verified end to end: 23 files written, 12 bundles, 12 derivatives, `check` clean,
and every URL the demo promises answers — including the alias redirecting, the draft
hidden, and the front page rendering through the site's *own* template override.
2026-08-01 02:23:37 +06:00
bdeshi e79aba2b11 add khosra new, which writes the first bytes into a site root
Scaffolds a **directory** bundle — the only shape that can own local files, so the
other kind would hand an author a page their pictures cannot live beside. What it
writes is a draft: title, today's date, `draft: true`. A tool that publishes the
moment it runs publishes by accident, and drafts are honoured now.

This is the first thing that writes into somebody's content directory, so it goes
through os.Root like every read does (ADR-0031), and it never overwrites: an
existing bundle is an error.

Two bugs found by running it rather than by testing it:

A key of `../escape` did not fail. It never left the site root — path.Join collapses
`..` first — but it wrote a real directory *inside* the root and outside content/,
which is not an escape and not a bundle either. Refused outright now, the same guard
the include path needed for the same reason. The test asserts what should be true —
content/ is the only thing this creates — because the weaker assertion I wrote first
would have passed.

`khosra new posts/x -site dir` silently ignored -site, because Go's flag package
stops at the first non-flag argument, and then failed complaining there was no site
root. Parsed in rounds now, so either order works.

main() crossed the function-length warning as a result, so it became a dispatch
table with runServe beside it — the warning was right about the code.
2026-08-01 02:23:37 +06:00