Use of semantics

Foundations made the claim: CTXR’s machinery carries no hardcoded knowledge of your types — it reads the semantics of the schema and acts on them. The Development chapter opens here, with the meaning itself, because everything that follows — the nodes, their relations, the engine that compiles them — is understood through it. Get the meaning straight and the rest is mechanical.

This is one of two halves. The semantic engine, on this page, declares what the schema means. The mechanical engine, later in the chapter, acts on it. The dependency points inward: a declaration is true with no executor, an executor is meaningless with no declaration — so the meaning comes first.

What the semantic engine records

For every property that points from one node to another, CTXR holds a small set of facts:

Fact What it says
range which types the property may point to (itemReviewed accepts a Thing)
domain which types may carry the property
inverse the property the relationship reads as in reverse (itemReviewedreview)
kind whether the link composes, aggregates, or links (see Relations)
cardinality whether it holds one value or many

The first three come straight from Schema.org. We don’t invent a data model; we reflect the one the vocabulary already defines. Where Schema.org is silent — it rarely declares an explicit inverse — the engine derives the missing fact rather than asking you to author it. The inverse of itemReviewed is written down nowhere; it’s the range index read backwards.

Meaning comes in three layers, and this is the middle one. One map says how a single property edits — its atom type, the leaf. Another says what fields a type carries — the node. This one types the relationship between nodes — the branch. Each is a single source of truth, so the picker, the validator and the compiler never disagree about what a property means.

Schema is a hint; the contract is the law

This is the distinction that keeps the engine honest. Schema.org’s range and domain are permissive hints — an open-world vocabulary saying what could go where, with no enforcement. They generate options: when you add a reference, the picker offers every type the range allows.

The contract is the closed-world layer on top — the kind, the cardinality, and any per-space narrowing. It constrains the selection: this property holds at most one value; in this space a Review may only review a Place. The rule that matters: narrowing is always a contract statement, never an edit to the range. The range stays as wide as Schema.org drew it; the contract decides what you actually accept. Confusing the two is a classic bug — pinning a property to one type by quietly shrinking the schema, instead of expressing the limit as a contract you can relax later.

Three reflected indexes

From those declarations the engine derives three lookups. They are schema-derived, not data-derived, so they live in memory and rebuild on demand — unlike the topology index, which tracks actual edges on disk.

forward range   →  given a property, which types may it point to?   (drives the outbound picker + validation)
inverse range   →  given a type, what may point at it?              (drives the inbound picker + derive routing)
domain          →  given a type, which properties may it carry?     (drives "add a property" on a fresh node)

The inverse range is just the forward range read the other way — one index, two directions. That single fact is what lets a Place “know” its reviews without anyone storing a list on the Place: the engine asks the inverse index what aggregates into a Place, and finds review.

With the meaning in hand, the next two pages are the things it describes — the typed records in Nodes & types, and the connections between them in Relations.