Network/Journal/Schema Driven Fact Rendering
All entries
Entry 035

Entity facts are registry data, not renderer code

Date
2026-06-13
Status
Decided
Authority
Creator

Decision

Each entity type's facts are governed by data in the schema registry, not by constants compiled into the renderer. The registry (schemas/registry) is the single source of truth for, per type:

  • which predicates are valid (types.<t>.valid / relationships / attributes),
  • how they're labelled (predicate_labels),
  • how variant spellings fold to a canonical key (attribute_aliases),
  • how they group into page sections (types.<t>.groups).

The public entity renderer builds its fact vocabulary from the registry at request time, over an in-memory cache that reloads when the registry changes — no restart, no redeploy. Adding or relabelling a fact type is a registry edit.

Why

A film page (solo-a-star-wars-story) displayed a near-empty body: one "Performer" and nothing else — no director, composer, producer, box office, runtime, writer, or cast — even though every one of those facts was present in the entity's data and declared valid in the registry's film schema.

The cause was an architectural shortcut: the renderer carried a hardcoded, character-shaped vocabulary (KEY_ALIASES / KEY_LABEL). Its own comment was explicit — any source key it didn't recognise "silently drops out of the merged view." The map knew actor → Performer, homeworld, lightsaber, species… but had no entry for any film predicate. So films, episodes, books, and games all lost their facts at the last step before render. The data layer was fine; the presentation layer was quietly throwing most of it away — and worse, fixing it meant editing and redeploying code every time a new kind of fact appeared.

That is exactly backwards for a knowledge graph whose whole job is to accrue new kinds of facts. The vocabulary of facts must be data the system curates, not a constant a developer maintains.

How

  • The registry already declared predicate_labels and per-type groups; the renderer simply didn't read them. It now does. Any predicate a type declares self-maps (survives the merge under its own name) and is labelled and grouped from the registry. The curated Wikidata-PID / semantic-folding base is kept as a fallback, extended by the registry rather than replaced.
  • The registry is read Firestore-primary with the GCS-mounted file as a safety fallback, cached in memory on a short TTL and refreshed fire-and-forget so rendering never blocks on a fetch. A registry edit in Firestore surfaces within the TTL.
  • The feedback loop that proposes new valid predicates already exists: the attribute-QA pass surfaces real-but-unregistered predicates per type as candidates. That becomes propose → approve → write-to-registry — all data.

Implementation: MB-404. The same change fixes every non-character type at once, because the gap was systemic, not film-specific.

Principle

The set of facts the platform can express is curated data, not compiled code. New knowledge shapes arrive as registry edits that take effect on a cache reload — never as a renderer patch and a deploy.