Skip to content
HomePagesHomePages template kit

Facts and provenance

Why a slot's value binds to a named source instead of copying a value at author time, and what that lets a deliverable survive across repeated fills and edits.

A fact is a normalized, canonical piece of truth about a property — assembled by the platform during intake, before your template is involved at all. By the time fill runs, the property already has a full account of itself, and nothing you author contributes to it. See the fact vocabulary for what is actually in that account; this page does not repeat it.

Your template reaches that account two ways, and both name it in one vocabulary. A slot can be bound to a single fact, which is what the rest of this page is about. A compute() callback instead reads the graph itself — every fact a binding can name, plus the pools no single slot could hold: the property’s photos, videos, tours, floor plans, documents, the notes distilled from them, and its always-on narrative. A fact carries the same container and the same name on both surfaces, so fact.unit.beds reads as f.unit.beds and there is no second vocabulary to learn.

Provenance is the other half. A slot does not just hold a current value — it holds a record of where that value came from: which fact, which other source, or a person’s own hand. Provenance is what makes the value explicable after the fact, and, as the next section argues, reconcilable.

A deliverable is not filled once. A property’s facts get corrected after intake runs. A user opens the editor and rewrites a headline. Later, someone reruns fill against the same deliverable — to pick up a fact that arrived late, or because a section was added to the composition after the fact. All of that has to happen to the same deliverable, repeatedly, over its life, and each pass has to leave the deliverable in a state that still makes sense.

That requirement is harder than it looks. Consider what a re-fill has to get right: a value the user already edited must not be silently overwritten — their edit is a deliberate act and re-running fill is not a reason to discard it. But a value nobody has touched should pick up a corrected fact, not keep serving a value that was already known to be wrong. Those are opposite behaviors for two slots that, looked at as bare values, are indistinguishable — both are just a string sitting in the same field.

They stop being indistinguishable the moment you ask not what a slot holds but how it got there. A slot bound to a source can be re-evaluated against that source and compared to what it produced last time; a slot a person overrode carries that fact instead, and re-evaluating it is exactly the wrong move. The distinction that resolves the re-fill problem is provenance, not value.

This is also why a binding declares a source rather than copying that source’s value in at authoring time. A copy is a snapshot: the instant it is taken, the link back to where it came from is gone, and nothing downstream can tell a copied value apart from one a person typed by hand. A declared source keeps that link alive — the platform can always ask “what does this source say now” and compare it to what is on the page. Provenance is what turns editing a deliverable from a one-shot render into something that can be revisited, corrected, and re-filled without losing what a person already did to it.

A slot’s value can arrive by one of a few routes: authored directly into the template by whoever built it, bound to a named source that fill resolves per deliverable, or produced by a decision fill makes from the property’s facts when no fixed source applies. A user’s edit is a further, separate route on top of any of those — it always takes precedence over whatever the slot was bound to, and the binding itself is not disturbed by the edit. Provenance is what distinguishes all of these from each other on a single slot: it does not just say what the value is, it says which of these routes produced it.

The binding itself is declared in the schema, naming a source drawn from the fact vocabulary — the same closed vocabulary discussed in Slots. Whether a given source has a value for a particular property is not something an author can guarantee in advance — the input material varies from one property to the next, and nothing forces every source to be present every time. A binding to a source with nothing behind it for this property does not fill the slot, so a template has to have a sane story for a slot with no value: the slot renders its marked empty branch, and a section that should not appear at all when the property cannot support it says so through its display.visibility. That is a decision an author makes at authoring time, because the platform cannot make it up on the property’s behalf.

Not a database you query at render time. By the time your renderer runs, sourcing is over — a slot’s value has already arrived on its props, resolved. Nothing in a template’s rendering code looks anything up; see what runs when in the pipeline.

Not a place for template-specific data. Facts describe the property, not the template. Content that belongs to the template itself — imagery, copy, or any other content that is the same for every deliverable — is either a static asset shipped with the template or a slot value authored directly, not a binding to a fact that does not exist. See Static assets.

Not a schema you extend. The vocabulary is a closed set the kit owns, for the same reason a slot’s kind is: an unrecognized name is a mistake to catch, not a value to leave silently unfilled. See why the slot vocabulary is a closed set.

Not a curated subset, either. Closed is not the same as narrow. Every fact the platform holds about a property is in the vocabulary by construction; what is held back is internal plumbing — a storage key, an id tying a derived row back to the upload it came from — which says where a value is kept rather than anything about the property. So the question to ask of something you want is whether it is a fact about the property at all, not whether the kit chose to expose it.