Skip to content

Foundry — Architecture, Rationale & Glossary


In brief (for leadership)

What it is. Foundry turns many noisy external feeds into one trusted, owned record of every team, player, and game — and simulates each game many times over into a full distribution of every possible outcome. It's the shared foundation the products are built on, not a product itself.

What it delivers. A canonical record of the sport plus, for any game, the complete distribution of simulated outcomes — every player and team result across many simulated runs — queryable for any outcome on demand, including novel or composite ones. What a product does with that distribution — price a market, project fantasy points, generate content — is the product's job, not Foundry's.

The bet. A vendor sells you data, or their answers. Foundry owns the canonical, event-sourced source of truth and runs our own models on it to produce the outcome distribution — the part you cannot buy. The feeds coming in are bought; the canonical core and the model that turns it into outcomes are built, because that core is the edge.

Where it stands. NFL-focused and sport-agnostic by design. The outcome distribution is not yet in production — model integration, accuracy instrumentation, and distribution are the work ahead, not re-architecture.


Part 1 — Why Foundry

The question

Why build Foundry instead of buying a sports-data feed (or a finished prediction product) and pointing the model at it — and why does it need this much machinery?

The short answer

A vendor sells you data, or their answers. Foundry produces a single canonical truth you own — and that truth, not the raw feed, is what lets us simulate every game into a full distribution of outcomes, queryable for any outcome on demand, consistently, for every consumer. A feed is an input to Foundry, not a replacement for it. Every architectural choice below is a cost paid to keep that truth trustworthy, queryable, and reusable.


Why event sourcing, not a plain database

A plain (CRUD) database stores only the current state: each update overwrites the previous value, discarding both the change and the reason for it. It answers one question — "what is the state now?" Foundry also has to answer "how did it get here, who did it, what else changed at the same time, and can I replay or reshape it?"

Event sourcing stores the changes (events) as the source of truth and rebuilds current state by replaying them, so nothing is lost. What that buys, that CRUD can't:

  • History & audit, for free. The ordered sequence of changes is itself a record; overwriting destroys it. Events are an immutable, time-ordered audit trail as a by-product of normal writes.
  • Real multi-editor safety. CRUD's only options are locking (one user blocks others) or last-write-wins (one user's change silently vanishes). Foundry uses optimistic concurrency: two non-conflicting edits both apply, and a genuine collision is rejected and retried — never silently merged or lost. (Precise mechanism in the engineer section.)
  • A smaller conflict surface — by design. Conflicts are bounded by the aggregate, not the row. Independent concerns are modelled as separate aggregates — a game's play-by-play, a player's injuries, the weather — so concurrent edits to different aggregates never collide; only two edits to the same aggregate do (and the loser retries). A single CRUD row has no such boundary: any two concurrent writes to it collide.
  • Rebuildable, reshapeable views. Every read model is fully reproducible by replaying the events. A new screen or report is a new shape replayed from history — no migration of the master data.
  • Many shapes from one source. The same history feeds several differently-shaped read models at once (one keeps order, another aggregates). In CRUD the table's shape is the read shape, so every new view is another hand-maintained derived table.
  • Time-travel debugging. You can reconstruct the exact state at any past moment to reproduce a bug. A state-only store never kept the intermediate states, so this is impossible.

How the data is modelled (and why it scales)

Event sourcing also changes what the unit of data is. The relational default splits everything into normalised tables and reassembles by joining on every read — the unit of design is the table. Foundry's unit is the aggregate: a self-contained cluster of data plus the rules that must always hold for it, loaded, changed, and saved as one piece.

That is what scales. A read or write touches one aggregate instead of joining many tables; and because separate aggregates don't have to stay transactionally consistent with each other, they can live on different servers/partitions without coordinating — horizontal scale-out. And integration falls out of the log: the event stream is a natural signal other parts react to (build a view, notify an external system, trigger a follow-up) — instead of bolted-on triggers, change-data-capture, or polling.


Why canonicalization, not just the vendor's IDs

A vendor gives you their IDs. Buy several feeds and you've bought several identity schemes that disagree — and every consumer would re-resolve them independently and drift. Canonicalization mints or maps one identity per real-world thing that you own and control, so every feed collapses to one record; anything already carrying a unique id needs no mapping. Without it, "the same player" is a different id in every product.

Why one command/read path for every actor

The model, traders, UIs, and automation all change and read through the same command → event → projection path. That is what makes changes consistent end to end, gives one audit trail across every actor, and applies the concurrency handling above to all of them — a trader and the model can never act on different versions of the truth. The cost (routing even a UI through commands) is what buys "everyone shares one truth."

Why contracts

Consumers depend on stable, shaped slices, not on Foundry's internals. Contracts — including the model input contract (model-owned, canonical, one per game) — let internals change without breaking consumers, and define the boundary where external signals are translated in.


The honest trade-offs

  • The read side lags the write side slightly (eventual consistency).
  • Views built from events can drift or fail, so they must be replayable and occasionally rebuilt.
  • Old events must be versioned/translated as their schema evolves.
  • More moving parts (messaging, view-building workers) and a steeper learning curve.
  • For simple, single-user, low-audit data a plain database is genuinely enough. Foundry is not that.

Risks (and what contains them)

  • A wrong canonical ID has blast radius. If canonicalization mints or maps an identity wrongly, every downstream consumer inherits it — one bad link poisons the shared truth. What contains it: the mapping lives in the crosswalk and the history is replayable, so a bad mint is traceable and correctable (remap the provider id to the right canonical id and rebuild), not silently overwritten as it would be in a CRUD store. Real risk — recoverable by design.
  • Key-person risk on the proprietary core. The model and engine are the edge and the IP; concentrated knowledge there is a genuine exposure. It's contained by the contract boundary around the model, documentation, and the accuracy instrumentation below — not something this doc can claim is already solved.
  • Model-accuracy risk. Foundry can produce a distribution for any outcome; whether that distribution is right is on the model. That's why accuracy instrumentation is called out as work ahead, not a footnote — it's the risk that matters most commercially.

Bottom line

Event sourcing — and the canonical platform around it — earns its cost exactly where Foundry lives: collaborative editing, strong audit needs, many views of one dataset, integration-heavy workflows, and a proprietary model whose outputs must be queryable for any outcome on demand. The edges can be bought; the core is the company's edge and must be owned. It's the difference between storing the latest answer and storing every decision that produced it.


Part 2 — Glossary

A plain-English reference for a leadership audience. Covers the terms that are specific to Foundry and don't mean what they sound like — the words worth defining before a senior review.


Product & concept

Term Definition
Foundry Luckbox's central sports-data platform. It ingests data from many outside sources, turns it into one trusted version of the truth, simulates each game into a full distribution of outcomes, and serves both out to every product. It is not a betting app or a consumer product itself — it's the shared foundation the products are built on.
Single source of truth / canonical identity Every real-world thing (a player, a team, a game) gets one stable Foundry identity, even though each provider has its own IDs for it. The canonical identity is the single agreed identity all those provider IDs map to — so everyone is talking about the same player, unambiguously.
Outcome distribution What Foundry produces for a game: by simulating it many times over, Foundry builds the complete distribution of every player and team outcome across all those runs (the Outcome Context). Rather than a fixed menu of pre-built answers, any outcome — including novel or composite ones — can be read straight from the distribution on demand. Turning a distribution into a price, a fantasy projection, or content is the consuming product's job, outside Foundry's boundary.

How it's built

The flow in one line: outside feeds come in → get reconciled to one identity → held as the trusted record → shaped per product → consumed by Luckbox products.

Term Definition
Three kinds of incoming data What Foundry ingests splits by what canonicalization does to it, not by speed. Master data — the things that must stay the same across sources and over time (teams, players, venues, competitions, and the game itself) — is reconciled to one canonical identity (see Map / Mint). Reference data — fixed vocabularies (positions, play types, scoring types) — is translated onto values Foundry already owns. Event data — the things that happen (plays, scores, injuries, weather) — is normalised into Foundry's canonical events on the Sporting Event Ledger. Cadence drives how each is plumbed — the slow Master Data Ingest Path vs the streaming Game Feed Ingest Path — but that is a separate axis from what canonicalization does.
External feed providers The outside data sources Foundry ingests from — for example Stats Perform, ESPN, FoxSports, and Genius Sports. Each has its own data model and IDs, and Foundry has limited or no control over them, so it treats them as untrusted input to be reconciled.
Feed Ingestion The inbound step between External feed providers and Canonicalization: per-provider ingesters — batch for master and reference data, streaming for game feeds (the two ingest paths) — that receive raw provider feeds and turn each record into a command. It moves data in and readies it for canonicalization; it does not interpret or own the data. (The component that converts a feed into commands is the Importer.)
Canonicalization Turning provider-specific data into Foundry's one canonical form. The operation differs by data class: master data is resolved to a single canonical ID (see Map / Mint); reference data is translated onto a value Foundry already owns (canonical by default, so nothing is minted); and a provider's play-by-play is normalised into canonical events on the Sporting Event Ledger, expressed in those same IDs. One inbound message can need more than one of these at once — a play references players (resolve) and positions (translate) while it is itself an event (normalise) — which is why the work is split by operation, not by feed. Incoming feeds are also standardised into one input shape (see Contract Canonicalizer).
Map / Mint (canonical ID) The identity step at the heart of canonicalization: for each incoming master-data record, Foundry either maps it to an existing canonical ID — recognising that one provider's "player 12345" and another's "athlete 99" are the same person — or mints a new one for a first sighting. The provider-ID-to-canonical-ID link is kept in the crosswalk, the mapping table that lets every provider's ID resolve to the one canonical identity.
Master Data The core real-world things of the sport that must each carry one shared identity, the same across every source and over time: teams, participants, venues, competitions, games. Canonicalization resolves each one to a canonical ID — so "master data" names the class, and a canonicalised entity is simply a master-data record after that resolution (the single owned record, not a second dataset). The counterpart is Reference Data, which needs no resolving.
Reference Data The fixed vocabularies a sport is built from — positions, play types, scoring types. Unlike master data, these aren't mapped per provider; they're canonical by default (a locked set Foundry already defines), so importers just translate each feed's labels onto them. That's why a position isn't resolved to an ID the way a participant is — there's nothing to resolve.
Contract Canonicalizer The inbound counterpart that canonicalizes data shapes rather than identities: it resolves each incoming feed into one standard input contract for downstream use — most importantly the contract the prediction model consumes. Distinct from the outbound Consumer contract, which shapes data going out to products.
Consumer contract The shaped slice of the canonical record that a particular product receives. Different products need different subsets, so rather than exposing everything, Foundry projects a tailored, stable contract for each consumer — defining what they get and in what shape, and insulating them from internal changes.
Data Distribution The outbound layer that streams Foundry's canonical data to those who need it — inside Foundry to the Model Orchestrator (Internal Distribution) and outside it to consumers (External Distribution). It is realised by the catalogs (Kafka topics that stream a store's data), the Consumer contracts that shape each product's slice, and the Integrations that push data downstream as it changes.
Luckbox Ecosystem Product Domain Consumers The Luckbox products that consume Foundry's output and apply their own domain meaning to it — for example fantasy, betting, content, and prediction markets (a Kalshi price request, say). This is where an outcome distribution becomes a price, a projection, or a presentation. Foundry owns the truth and the distribution; these consumers decide what it means for their product. They sit outside Foundry's boundary.
Integration A reusable mechanism that reacts to Foundry events to push data downstream as changes happen — wiring Foundry's output into another system — rather than a one-off pipeline built per case. (The inbound equivalent, turning a feed into commands, is the Importer.)
Automation System An automated, non-human actor that drives Foundry or consumes its outputs — the machine-driven counterpart to an operator working through the UI.
External signals Market prices and other outside information are processed outside Foundry, not ingested as truth. A signal enters only when someone decides to act on it — at that point the decision is translated into the Model Inputs contract and applied through the normal command path. A raw signal never moves the model on its own; a deliberate change does.
Input Control The governed entry point for deliberate inputs into the pipeline — operator and Automation System decisions, and External signals that someone has chosen to act on. Such inputs reach Canonicalization and the Model Orchestrator only through the command path (as Model Inputs or other commands); a raw signal never moves the system on its own.
Subsystem (Sport / Infra / UI) The top-level structural divisions of the platform. The Sport Subsystem handles the sports-data and prediction domain; the Infra Subsystem provides shared platform plumbing; the UI Subsystem covers the operator-facing interfaces.

Out of scope for this glossary: the RFQ path — how price requests (e.g. Kalshi) are ingested and responded to inside Foundry — is deliberately not defined here.


What it models

Foundry models two things: the real-world structure of the sport, and the operational constructs it builds to run prediction and distribution. Each sport record gets a single canonical identity, so it's the same team, player, or game everywhere it appears. Only the misread-prone terms are defined here (the fuller set is in the technical breakdown below); the operational side starts with Model Inputs and will grow as more is modelled.

Term Definition
Competition A league or tournament — for example the NFL.
Season One running of a competition, such as the 2026 NFL season. A season can be structured into groupings such as conferences and divisions.
Squad A team's full registered roster for a season.
Lineup The specific players selected for a single game — the team sheet.
Participant A person in the sport: a player, coach, or official.
SportingEvent A single game or match between teams.
Sporting Event Ledger A game's event stream within the Internal Domain Event Store (one stream per SportingEvent): the complete, ordered record of everything that happened in a game, stored as a sequence of canonical events as they occur — the provider's play-by-play already normalised into Foundry's form, not raw feed data. It is the source of truth for a match — the full event-by-event history — as distinct from the live game-state view derived from it.
Canonical ID The one permanent identifier Foundry assigns to each master-data record. Every provider's own ID for it maps to this one, so it keeps one identity for life regardless of source.
Model Inputs The standardized figures the prediction model runs on for a game — e.g. each side's expected points and the projected total — assembled by canonicalizing the incoming feeds (box score, game state, commentary) into one input contract (see Contract Canonicalizer).

The prediction engine

How Foundry turns a live game into a full distribution of simulated outcomes.

Term Definition
Model Orchestrator The component that runs a game's simulations: it feeds Game State to the prediction model, drives the many simulated runs, and journals the outcomes that become the Outcome Context.
Game State A projection: a current snapshot of a game (score, clock, situation), derived from the Sporting Event Ledger. It is the model's read of the live match — one of the inputs the prediction model runs on.
Predictors The individual prediction components the model calls as it simulates a game. Each answers one specific question — for example, what happens on the next play — and a single simulated game makes many predictor calls. Together they drive the simulation forward.
Outcome Journaling Recording the model's outputs as they are produced, so they are retained ready to be assembled into the Outcome Context, served, and queried.
Outcome Context The full set of simulated results for a single game, laid out so any question can be answered against it. Foundry simulates the game many times over, and the Outcome Context holds every player and team outcome across all those runs. It is what lets any outcome be answered on demand — which a product then prices, projects, or presents.
Outcome Context Management The layer that captures, holds, and serves the model's outputs: Outcome Journaling records each run, the Outcome Context Store holds the full distribution, and the ExternalOutcome Query Service answers any outcome on demand for consumers. What it manages is the Outcome Context itself.
ExternalOutcome Query Service The read interface over the Outcome Context Store: it answers any outcome — novel or composite — on demand by reading the stored distribution rather than re-simulating, and is how a consumer (a pricing or fantasy product) pulls the outcomes it needs.

For engineers — domain model

The records behind the leadership terms above, with the detail trimmed from the plain-English read. Master-data records each carry a canonical id; the other groups change frequently, are derived, or are platform constructs.

Group Record Canonical id What it is
Master data Competition CompetitionId A league or tournament, e.g. the NFL.
Master data Season SeasonId One running of a competition.
Master data Pool PoolId A season's structural grouping — conference or division.
Master data Team TeamId A club or franchise.
Master data Participant ParticipantId A person: player, coach, or official.
Master data Venue VenueId A stadium or ground.
Master data SportingEvent SportingEventId A single game — the fixture identity.
Reference data positions, play types, scoring types Fixed vocabularies; canonical by default, never mapped per provider.
Transactional / event data Squad A team's registered roster for a season; changes through the season.
Transactional / event data Lineup The players selected for a single game.
Transactional / event data Player injury InjuryId A participant's injury and availability; updates frequently.
Transactional / event data Match weather WeatherId A game's forecast and conditions; updates independently of the fixture.
Transactional / event data Sporting Event Ledger A game's event stream within the Internal Domain Event Store — ordered canonical event history, the source of truth, keyed by SportingEventId.
Transactional / event data Game State A live projection of the ledger — score, clock, situation.
Operational constructs Model Inputs The standardized figures the model runs on for a game, assembled by the Contract Canonicalizer.
Operational constructs Model Config Configuration and controls for how models are set up and run.
Operational constructs Consumer contract The shaped output slice each product receives.

For engineers — event-sourcing building blocks

Skip for a leadership read. These are the write-side terms drawn on the architecture diagram, included so the doc carries both the plain-English layer above and the precise building blocks here. The chain: a command expresses intent → an aggregate decides → a domain event records what happened → the ledger stores it and projections are built from it.

Term Definition
Command A request to change state, named as an instruction (e.g. RecordPlay). It's intent, not fact, and can be rejected. Ingest/Import turns each incoming record into one.
Aggregate The consistency boundary that receives a command, checks the rules against its current state, and emits the resulting domain events. Its own state is rebuilt by replaying those events.
Domain event An immutable fact recording something that happened (e.g. PlayRecorded). Once written it never changes; it is both stored (in the Internal Domain Event Store — a game's stream within it is its Sporting Event Ledger) and streamed.
Event handler Code that reacts to a domain event — usually to update a projection or trigger a downstream action.
Projection / read model A queryable view built from the event stream, shaped for one read need (the catalog, Game State, a consumer contract). Disposable and rebuildable; never the source of truth. (The projection-vs-contract distinction is drawn out below.)

Concurrency (optimistic, version-checked). Every aggregate carries a version that increments with each event. A command runs against the version it last read — its expected version — and the append only succeeds if that version is still current. A conflict is two commands issued from the same version: the first appends and bumps the version; the second then fails the check. Policy: the loser is rejected (a concurrency error) and the caller reloads the aggregate and retries its intent against the new state — no lost update, no server-side merge. The boundary is the aggregate: edits to different aggregates never collide (independent versions), while two commands on the same aggregate from the same version always do — regardless of which fields they touch.

Replay & snapshots. "Rebuildable by replaying" has two backstops the value prop relies on. Snapshots: an aggregate periodically persists its current state, so loads and rebuilds resume from the latest snapshot plus the handful of events since — not from event 0. Side-effects on rebuild: a rebuild reconstructs read state only. It does not re-fire integrations or notifications — those side-effecting handlers run on live events and are idempotent, so replaying a projection never re-sends an email, re-pushes a feed, or re-triggers a downstream action.

For engineers — aggregates (write side)

Each aggregate is the write-side boundary for one record: it receives commands, enforces the rules, and emits the domain events that become the source of truth. It's keyed by that record's canonical id and rebuilds its own state by replaying its events. The read side (projection → contract) is built downstream from those same events.

Aggregate Keyed by What it owns / enforces
CompetitionAggregate CompetitionId a competition's identity and details
SeasonAggregate SeasonId a season and its pool structure — enforces placement, leaf and uniqueness rules
TeamAggregate TeamId a team's identity and details
ParticipantAggregate ParticipantId a participant's identity and details
VenueAggregate VenueId a venue's identity and details
InjuryAggregate InjuryId a participant's injury and availability over time
WeatherAggregate WeatherId a game's forecast and conditions over time
SportingEventAggregate SportingEventId a game's lifecycle and recorded play-by-play

Simple vs complex, the same split as the read side: VenueAggregate is about as small as it gets — a flat record with little to enforce — while SeasonAggregate is complex, owning a nested pool tree and rejecting any command that would break its structural rules. (Core sport set, not exhaustive.)

For engineers — projections & read-model contracts

The read side has one builder and two kinds of contract — easily conflated, so pinned down here:

  • a projection is the builder — code that folds the event stream into a queryable document and keeps it current;
  • a read-model contract is the internal shape that builder produces — the document a Foundry reader gets back (e.g. VenueContract, SeasonContract);
  • a consumer contract is the external shape — the per-consumer slice handed to a product outside Foundry (built by the External Consumer Contract Projector), kept deliberately stable so internal change can't break consumers.

Both contracts are projections of the event log; they differ by audience (internal read vs external product) and stability guarantee. Everything here is disposable and rebuildable; the domain events (a game's stream is its Sporting Event Ledger) stay the only source of truth.

Example Read-model contract (the shape) Projection (the builder) What it folds
Simple — Venue VenueContract — a flat record: id, name, location VenueContractBuilder (a single-stream projection) one venue's own events; small and stable
Complex — Season SeasonContract — season details plus a nested pool tree (conferences/divisions) SeasonContractBuilder (a single-stream projection) several event types (season created, structure defined, …) assembled into one structured document

A read model can also fold many streams at once (a multi-stream projection) — e.g. rolling every game a player appeared in into one player-stats contract — but the split is the same: the contract is the shape, the projection is what builds it.

Worked example — a live game (all three layers). Each recorded play is a domain event in the Sporting Event Ledger (the write-side truth). Projections fold that stream into read-model contracts — a Game State (live score/clock/situation) and a Box Score (running totals), the internal shapes Foundry queries. A consumer contract then derives the external slice each product needs: the fantasy consumer gets scoring fields, a content consumer a presentation shape — held stable so internal change can't break them. Truth → projection → read-model contract → consumer contract. The simple case runs the same chain, trivially: Venue's one projection yields a VenueContract (read-model), exposed to a product as a venue consumer contract — every layer present, just tiny.

Versioning contracts. Read-model contracts evolve cheaply: add a field → extend the projection → replay rebuilds it, with no data migration (that's the payoff of rebuildable views). Consumer contracts are the careful ones, because outsiders depend on them — additive changes ship in place, but a breaking change ships as a new contract version served alongside the old, and each consumer migrates on its own schedule before the old version is retired. Internal change never forces a consumer break; that's what the contract boundary is for.

Stores & catalogs (where it lives). A store is a database; a catalog is a set of Kafka topics that stream a store's data to consumers (internal and external topics) — e.g. the Canonical Master Data Catalog and Sporting Event Ledger Catalog internally, and the External Canonical Catalog that publishes the canonical record outward to consumers. The store persists; the catalog distributes. There are only three stores, each with a distinct role — one source of truth plus two derivations, never the same data many times over: - Internal Domain Event Store — the write-side event log and single source of truth: one logical store of per-aggregate event streams (a game's stream is its Sporting Event Ledger); any sharding or tenanting is a physical deployment detail. Internal — never referenced externally. - Read Projection Store — the CQRS read models built from those events; derived and rebuildable. - Outcome Context Store — the simulated outcomes the model produces; a derivation, rebuildable by re-running the pinned model + seed against the ledger state that produced it. So store → catalog is persistence plus distribution, and store-to-store is one truth plus its derivations — neither is duplication.

For engineers — the Model Orchestrator

Where the proprietary value lives — so the pipeline around it is worth precision. The orchestrator turns the event-sourced truth into the outcome distribution; it is a read-side consumer (its inputs are projections of the Internal Domain Event Store, its outputs are journaled to the Outcome Context Store) — it never owns truth. The model's internals are proprietary; what's specified here is the surrounding pipeline, not the model's mechanics.

Stage Component(s) What it does
Consume events Canonical Sporting Event Ledger Event Processor follows a game's ledger stream as it advances
Consume inputs Canonical Model Input Event Processor follows changes to the model-input contract
Prepare Game State Translation · Model Input Translation shape ledger + inputs into what the model consumes
Run Model RunnerEngineModel · Predictors drive the simulation; each predictor answers one question (e.g. the next play) across many runs
Record Outcome Journaling → Outcome Context Store journal every run's player/team outcomes into the Outcome Context
Configure Model Config the version/config that governs a run

How a run is triggered. The orchestrator reacts; it does not run on a clock. A new ledger event (the game advanced — e.g. a play recorded) or a change to the model-input contract (a deliberate decision to re-simulate) wakes the matching event processor, which re-runs that game and nothing else.

What a run does. Each run simulates the rest of the game many times over — every simulated play-out is a world, and the world count sets how many. Across those worlds the model produces a full distribution of every player and team outcome; that distribution is the product Foundry serves. The engine driving it (the bitvector engine, sequence context, and the predictors) is the proprietary core — named here, not specified.

What a run produces. Every outcome from every world is recorded by Outcome Journaling into the Outcome Context (Outcome Context Store), keyed by outcome_id. Because the whole distribution is already stored, any outcome — including novel or composite ones — is answered on demand by reading the context (via the ExternalOutcome Query Service), not by re-simulating. Turning that answer into a price or a projection happens in the consuming product, outside Foundry.

Reproducibility. A run is deterministic given (ledger state, config, seed): Model Config pins the model version, parameters, and the RNG seed, so any stored outcome can be regenerated exactly by re-running the pinned model against the ledger state at that point. That determinism is what makes the Outcome Context a genuine derivation (rebuildable), not a separate source of truth.