# Theme contract
What the engine promises a theme, and the only thing this repository is bound to (ADR-0023). A theme's
markup, layout and styling are not the engine's business; a theme's *inputs* are.
**STATUS: partly live.** The fields under *Live today* exist and are gated; everything else is the shape
the contract takes when the feature arrives. All of it is engine obligation, not theme instruction — a
theme may ignore any of it.
## Live today
A bundle page receives:
| Field | Contents |
|---|---|
| `.Title` | may be empty; a template falls back to `.Key` rather than failing |
| `.Lang` | the locale of this variant, always set |
| `.Key` | the bundle's identity, without language or extension |
| `.HTML` | the rendered body, already escaped |
| `.Extra` | every frontmatter key the parser does not name (ADR-0002) |
| `.Style` | the reference theme's stylesheet, inlined so a bare site root needs no asset route |
| `.Canonical` | the permalink of the variant actually served — not the URL requested, which differs when the fallback chain supplied another language |
| `.Alternates` | every language this key exists in, as `.Lang` and `.URL`, for `hreflang` |
Two named templates: `base` is executed for every page; `main` is the block a theme redefines to change
the body while inheriting the document. Nothing else is promised yet.
## The stability rule
Fields and names are **added, never renamed or removed**. Absence is always legal: a template reading a
field that does not exist gets the zero value and must not crash, and the engine must not make a missing
field fatal at request time (ADR-0002, invariant 1). This is the View-layer freeze from
`architecture.md`, arriving as soon as a theme exists rather than at Arc 2.
Breaking the contract is not a feature — it is a new contract version, and it needs an ADR.
## What the engine provides
| Provides | Detail |
|---|---|
| the page object | known fields plus an `Extra` bag carrying unknown frontmatter (ADR-0002) |
| the resolved cascade | settings after site → section → bundle resolution (`ideas/deferred-decisions.md`) |
| queries the page needs | its sequence neighbours, its taxonomy terms, its section's members |
| named template lookup | per-type sets; a theme redefines a named block and inherits the rest (ADR-0019) |
| URLs | every path the engine emits, so a theme never constructs one by hand |
| per-page assets | the `styles` / `scripts` frontmatter lists, resolved relative to the bundle |
| chrome strings | looked up by key and language, never hardcoded English in a template |
| validity windows | a template that renders time-dependent output declares one |
## Extras view
For a request under a bundle's extras directory (`ideas/deferred-decisions.md`) the engine additionally provides:
| Field | Contents |
|---|---|
| `.Extras.Entries` | the tree: `Name`, `Path`, `URL`, `RawURL`, `Kind`, `Size`, `IsDir`, `ModTime` |
| `.Extras.Selected` | nil on the bare listing; otherwise the chosen entry |
| `.Extras.Selected.HTML` | rendered output for `markdown` and `text` kinds; empty otherwise |
| `.Extras.Selected.RawURL` | always present — for ``, `