generate sized derivatives ahead of the request
A pass over the content at startup writes three widths per picture into a cache outside the site root, named by the source's content hash and the width (ADR-0042). Idempotent by construction: a rerun stats and skips, an edited picture takes a new name, and nothing stale can be served under an old one. Restarting the evidence site made 0 derivatives the second time, as it should. Ahead of the request rather than during it, because resampling is felt and there is no page cache yet to hide it. Outside the site root, because the engine reads that directory and must not leave generated files in somebody's content git — a lost cache costs one startup pass and no correctness. Markup now carries the original as src, the derivatives as srcset closed by the original at its own width, and width/height from the original — which retires most of the latent row about the output floor; only a gallery's alt is still empty, and a filename cannot supply that. Two things the work itself decided: `Fragment.Items` became `Fragment.Pictures`, ADR-0037's own revisit trigger. Items had one consumer, so widening it beat adding a second list beside it. "A browser can show it" and "we can resample it" are different questions, and conflating them nearly deleted content: an SVG has no decoder here, so a single predicate would have dropped SVGs from galleries silently. Undecodable and unsupported pictures are now rendered as they are, without a size or a srcset.
This commit is contained in:
@@ -550,3 +550,28 @@ going there: if they land in core, `EXT_LOC` measures nothing and invariant 9 be
|
||||
the whole reason there are two ceilings.
|
||||
Revisit if: core approaches 2800. That is the question "what here is not core?" and the answer is a leaf, not
|
||||
a third raise.
|
||||
|
||||
## ADR-0042 — Derivatives are generated ahead of the request, into a cache outside the site root
|
||||
Date: 2026-07-30 · Status: accepted (replaces `Fragment.Items` with `Fragment.Pictures`, ADR-0037's own
|
||||
revisit trigger)
|
||||
Decision: sized image derivatives are produced by a pass over the content at startup, not during a request,
|
||||
and written to a cache directory outside the site root (`-cache`, defaulting under `os.UserCacheDir`). Each is
|
||||
named by the source's content hash and the target width, so the pass is idempotent and a changed source yields
|
||||
a different name. The engine never writes into the site root and never touches an original. Core serves that
|
||||
cache as a directory of opaque names; every image decision — widths, naming, dimensions, which files are
|
||||
images — lives in `internal/ext/shortcodes`, the package whose shortcodes need it. A fragment now receives
|
||||
`Pictures`, each carrying `Src`, `Srcset`, `Width` and `Height`, replacing the bare `Items` list.
|
||||
Why: resampling on the request path would make the first view of a page take seconds, and there is no page
|
||||
cache yet to hide it. Writing derivatives into the site root would put generated files in somebody's content
|
||||
git — the engine reads that directory and must not litter it, and derived state is disposable by definition
|
||||
(ADR-0010). Content-addressed names mean a rebuild rewrites nothing, and a lost cache costs one startup pass
|
||||
rather than any correctness. `Items` had exactly one consumer, so widening it in place beat adding a second
|
||||
list beside it.
|
||||
Consequence: cheap — no request pays for resampling, the cache can be deleted at any time, and `width`/`height`
|
||||
in the markup end the layout-shift problem the output floor named. Expensive — a new image needs a restart
|
||||
until change detection lands (queue 21), the cache is a second directory to think about when deploying, and
|
||||
AVIF passes through unresized since nothing can decode it. A feature still cannot serve a route of its own, so
|
||||
core carries a generic "serve this directory of derived files" — which is the seam to revisit when a second
|
||||
feature wants output of its own.
|
||||
Revisit if: startup time becomes noticeable on a large site — then the pass wants a manifest and a change
|
||||
check rather than a stat per candidate.
|
||||
|
||||
Reference in New Issue
Block a user