record ADR-0036: shortcodes render through theme templates

Two things already recorded settle the shape. The theme contract says the engine
decides nothing about how content looks, "including how media is embedded", so a
<figure> assembled in Go would be the engine dressing content. And invariant 2
wants the trusted/untrusted split as real code, not goldmark's default.

Both are satisfied by the same design: parse the shortcode into an AST node, then
render it by executing a theme template of that name. Raw HTML stays disabled, so
every byte of HTML on a page came from a template the site owns, and an author's
text survives only as arguments that html/template escapes.

Recorded before the code because the syntax authors type is a disk contract, and
because a feature that cannot render itself needs a partial-rendering function
passed in at wiring time — a consequence worth agreeing to in advance.
This commit is contained in:
2026-08-01 02:23:35 +06:00
parent d80885a412
commit 47df821056
+21
View File
@@ -440,3 +440,24 @@ can be added or dropped without touching a URL. Expensive — a Bengali reader s
changing that later needs this decision reversed plus an alias for every path already published. changing that later needs this decision reversed plus an alias for every path already published.
Revisit if: Bengali becomes the dominant language of the site — the trigger ADR-0009 already names. That Revisit if: Bengali becomes the dominant language of the site — the trigger ADR-0009 already names. That
is a default-language change, not per-variant slugs. is a default-language change, not per-variant slugs.
## ADR-0036 — Shortcodes parse to engine-built nodes and render through theme templates
Date: 2026-07-30 · Status: accepted
Decision: a shortcode is `{{< name key="value" >}}` alone on a line, parsed into an AST node by a goldmark
block parser. It renders by executing a theme template of the same name with its arguments as data — the
engine supplies no markup — and raw HTML in Markdown stays disabled, so the only HTML on a page comes from
a template the site owns. A shortcode whose template is missing logs and renders nothing rather than
failing the request.
Why: two constraints meet here and both are already recorded. `theme-contract.md` says the engine decides
nothing about how something looks, "including how media is embedded", so a `<figure>` built in Go would be
the engine dressing content. And invariant 2 needs the trusted/untrusted split to be real code rather than
goldmark's default: with `html.WithUnsafe()` still off, an author's raw HTML is dropped while a
shortcode's output is trusted *because a template produced it*, and the author's bytes survive only as
arguments, which `html/template` escapes.
Consequence: cheap — markup lives where markup belongs, a theme restyles a shortcode by redefining one
template, and the escaping is the standard library's rather than ours. Expensive — a feature under
`internal/ext` cannot render itself, so it is handed a partial-rendering function at wiring time
(`cmd/khosra/wire.go`), and the shortcode's argument names become part of the theme contract, additive
only.
Revisit if: a shortcode needs to emit something no template can express. That is an argument for a new
contract field, not for the engine writing HTML.