hide drafts and future-dated bundles; reveal them with -dev on

A draft is now not served at all, and neither is a bundle whose date has not
arrived. The filter sits in `Site.Lookup` and `Site.Run`, which is every path to a
bundle — so the files inside an unpublished bundle inherit its status for free,
which is what ADR-0024 asks for and what the asset route was written to allow. A
test asserts the 404 for the bundle *and* its picture, and that nothing leaks into
a listing, a feed or a sitemap.

The clock is read per request rather than at startup, so a scheduled post appears
exactly when its date arrives with nothing to restart and nothing to invalidate.
That first clock read created internal/content/clock.go, which is the only place
`verify.sh` allows `time.Now` — a render that depends on the time is worth being
able to find.

`-dev on` reveals both and reparses the theme before each render. Deliberately not
a bare boolean flag: turning unpublished work into public work should not be one
fumbled argument away. A reload that fails to parse leaves the working template set
in place, so a typo shows an error rather than replacing a good set with a broken one.
This commit is contained in:
2026-07-31 12:47:44 +06:00
parent 12f9a31779
commit 0231dae3b7
8 changed files with 202 additions and 13 deletions
+9 -3
View File
@@ -69,7 +69,8 @@ overrides the defaults the binary embeds, so a bare root still renders.
- A directory starting with `_` other than `_index` is ignored, and so is a **file**: `_tools.md` is a
fragment, not a bundle. That is how a file meant only to be included avoids taking a URL of its own,
appearing in its section's listing, and turning its bundle into a one-member series.
- `draft: true` is excluded from queries and feeds but reachable at its own URL in dev.
- `draft: true` is not served at all: not in a listing, a feed or a sitemap, and not at its own URL. Neither is
any file inside its bundle (ADR-0024). `-dev on` is the only thing that changes that.
## Frontmatter
@@ -271,8 +272,13 @@ asset, everything under extras (ADR-0024). An unpublished bundle answers **404**
assets; 403 would confirm the work exists. `-dev` reveals drafts and future-dated bundles with their
assets, defaults off, and is the only thing that changes the answer.
For a future-dated bundle the 404 expires at its publish time, so it becomes visible
exactly when the bundle becomes public.
For a future-dated bundle the 404 expires at its publish time, so it becomes visible exactly when the bundle
becomes public — the clock is read per request, so nothing has to be restarted or invalidated for a scheduled
post to appear.
`-dev on` reveals drafts and future-dated bundles and reparses templates before each render, so editing a
template needs no restart. It is off by default and is not a bare boolean: revealing unpublished work should be
impossible to enable by fumbling an argument.
## Typography and localisation
+7 -4
View File
@@ -10,8 +10,9 @@ If this file disagrees with the code, the code is right and this file is a bug.
| `go.mod` | module `khosra`; `goldmark`, `x/text`, `yaml.v3` direct | 10 |
| `internal/content/doc.go` | package comment | 5 |
| `internal/content/content.go` | bundles: `os.Root` open, walk, frontmatter split, key/lang derivation, NFC, tag slugs, partial files, permalink building | 381 |
| `internal/content/clock.go` | the one place the engine reads the wall clock, which `verify.sh` enforces by filename | 12 |
| `internal/content/settings.go` | `site.yaml`: the site's own declarations (`base`, `title`) and absolute-URL building (ADR-0039) | 59 |
| `internal/content/site.go` | the indexed site: lookup with language fallback, aliases, `Query` and `Run`, sections, `Sequence`, `Everything`, slug routes | 395 |
| `internal/content/site.go` | the indexed site: lookup with language fallback, aliases, `Query` and `Run`, sections, `Sequence`, `Everything`, slug routes, publication visibility | 424 |
| `internal/render/render.go` | goldmark with the typographer, per-kind template sets with site override, the `Partial`/`Origin` seams features render and resolve through, `Page`/`List`/`Sequence`/`head` | 409 |
| `internal/render/chrome.go` | the engine's own words: phrase table, month names, digits, and the `t`/`num`/`day` template funcs (ADR-0034) | 105 |
| `internal/render/templates/` | reference theme: `base.html`, `page.html`, `list.html`, `shortcodes.html`, `theme.css` (ADR-0026) | — |
@@ -23,9 +24,9 @@ If this file disagrees with the code, the code is right and this file is a bug.
| `internal/web/feed.go` | Atom for the site, a section or a tag, from dated bundles via one Query (ADR-0043) | 125 |
| `internal/web/discover.go` | `/robots.txt` and `/sitemap.xml`, absolute and only with a declared base (ADR-0039) | 74 |
| `internal/web/web.go` | handler: resolve, look up with fallback, section and tag listings, sequence, `/static/` (misses and refusals alike answer 404), degrade on failure | 152 |
| `cmd/khosra/main.go` | flags (`-site`, `-addr`, `-base`, `-cache`), wiring, startup including the derivative pass — the only place things are assembled | 92 |
| `cmd/khosra/main.go` | flags (`-site`, `-addr`, `-base`, `-cache`, `-dev`), wiring, startup including the derivative pass — the only place things are assembled | 92 |
| `cmd/khosra/check.go` | the `check` subcommand: parse, print, exit code. What counts as a finding lives in the feature | 45 |
| `*_test.go` | table-driven, one file per source file; symlink escape (content and static), canonical paths, language fallback, aliases, pagination, tags, sequences, chrome, typography, shortcode escaping, galleries, includes, partials, site settings, absolute URLs, robots, sitemap, slug routes, bundle assets, derivatives, feeds, 404, plus benchmarks for the render path and the checker | 2608 |
| `*_test.go` | table-driven, one file per source file; symlink escape (content and static), canonical paths, language fallback, aliases, pagination, tags, sequences, chrome, typography, shortcode escaping, galleries, includes, partials, site settings, absolute URLs, robots, sitemap, slug routes, bundle assets, derivatives, feeds, 404, plus benchmarks for the render path and the checker, unpublished visibility | 2608 |
Serves a bundle at `/{section}/{slug}/` — the slug derived, or declared in frontmatter without moving the
key (ADR-0035) — a paginated listing per section, tag listings global and
@@ -34,7 +35,9 @@ URL, generated derivatives under `/derived/`, Atom feeds per site,
section and tag, plus `/robots.txt` and `/sitemap.xml`.
Chrome text, dates and digits render in English or Bengali; authored text is untouched but for typographic
smoothing (ADR-0034); line breaking is left to CSS (ADR-0045). This repo holds engine source only — the site root is external and passed with
`khosra check` validates a site root and exits non-zero on anything that makes it wrong.
`khosra check` validates a site root and exits non-zero on anything that makes it wrong. A draft or
future-dated bundle is not served at all — nor is any file inside it (ADR-0024) — until `-dev on` reveals it and
reloads templates per request.
`-site` (ADR-0011). `site.yaml` declares `base` and `title`; with a base, canonical, hreflang and OpenGraph
URLs go absolute (ADR-0039).