1. Data and the value model
This chapter defines what data is, independent of how it is written. Chapter 2 writes it as text and chapter 3 as JSON; both MUST express exactly the model here, so either form converts to the other without losing a pair.
1.1 Records #
A record is one thing: a product, a unit, a film, a person, a booking. It has an envelope and values.
The envelope is defined by the core and written by the engine. No person or contract writes it directly, and it carries no source.
| Field | Holds | Notes |
|---|---|---|
id |
the record's identifier | unique within its owner; never reused |
kind |
the record's root type | fixed when the record is created (1.2) |
level |
which of the kind's levels this record sits at | product, unit … (1.2) |
of |
the record one level up, by ID | absent at the top level |
owner |
who controls the record | a domain (mediaencyclopedia.com) |
wears |
the classes the record wears, in order | classes refine the kind (chapter 5) |
created · updated |
times, UTC | RFC 3339, Z |
parts |
owned parts (1.6) | absent when there are none |
sources |
where each value came from (1.9) |
Identifiers. An ID is one or more of A–Z a–z 0–9 - _ ., starting with a letter or digit. Inside one
owner's data a bare ID names that owner's record. Another owner's record is written prefix:ID, where the
package maps the prefix to the owner (chapter 11). IDs are case-sensitive.
1.2 Kinds and levels #
A kind is a record's root type. A package declares its kinds; the core defines none of them. Each kind declares:
- its levels, top to bottom (an item kind:
group > product > unit; a kind with one level names that level after itself); - a base class every record of the kind wears;
- its schema.org type (
schema:Product), for publishing.
Level names MUST be unique across the kinds of one package, so a level names its kind. A record's level MUST
be one of its kind's levels, and its of MUST name a record of the same kind at the level directly above.
1.3 Keys #
A key names a facet. Its grammar:
key = [ prefix ":" ] name
prefix = lower *( lower / digit / "-" )
name = ( ALPHA / "_" ) *( ALPHA / DIGIT / "_" )
- A bare key belongs to the document's own package. Any other package's key MUST carry a prefix.
- A prefix is a local nickname. Each package (and each border wrapper) maps its prefixes to full package
identities,
domain/name(chapter 11.1). Two keys are the same key when their prefixes map to the same identity and their names are equal; readers MUST compare after resolving, never by the nickname. - Keys are case-sensitive and MUST be in Unicode NFC. Other spellings (aliases) are folded away before storage (chapter 4); stored data never holds an alias.
- A key never begins with
$, so$is free for the JSON form's tags (chapter 3). - A key the reader has no definition for is an unknown key. Its value is kept, as written, and never interpreted (1.4.9).
1.4 Values #
A value has one of the shapes below. The facet decides which shape a key's value takes: chapter 2's lexical shapes only suggest. A value that doesn't fit its facet is a type contradiction (chapter 9), not a different type.
1.4.1 Text #
A sequence of Unicode characters in NFC. Text is compared exactly; case-folding and trimming belong to aliases and contracts, not to text.
1.4.2 Number #
A decimal number: an optional -, digits, and an optional . and digits. No exponent, no +, no leading
zeros except a lone 0 before the point. Implementations MUST keep the value exactly as a decimal (no
binary floating point) and MUST compare by numeric value (2.50 equals 2.5). A facet may limit places and
range (chapter 4).
1.4.3 Quantity #
A number with a unit: 97 min, 2.4 lb. The unit is a spelling from the units table. A quantity is a
number facet's value when the facet names a unit. Two quantities are equal only with the same unit;
conversion is a facet setting (chapter 4), never implicit.
1.4.4 Money #
A number with an ISO 4217 currency: 12.50 USD. Money is a number facet whose unit is a currency. The amount
is an exact decimal with any number of places; conversion to minor units (cents) happens only at a border
whose contract asks for it. A three-letter uppercase spelling is always a currency, so the units table MUST
NOT use one as a unit.
1.4.5 Date #
A calendar date, proleptic Gregorian, at one of six precisions: year (1997), month (1997-10), day
(1997-10-03), and with a time of day, hour (2026-11-02T14Z), minute (2026-11-02T14:30Z) or second
(2026-11-02T14:30:15Z). The precision is part of the value: 1997 and 1997-01 are different values. A
facet says which precisions it accepts (chapter 4).
- A date with a time is a moment and MUST carry its UTC offset:
Z, or+hh:mm/-hh:mm(2026-10-14T18:00-05:00). Canonical form writes a zero offset asZ. Two moments compare by the instant they name, whatever their offsets. - Local wall-clock dates ("doors at 7pm, wherever the venue is") carry no offset, and are allowed only
under a facet with
zone: local. They compare only with other local dates; comparing one with a moment is undetermined (chapter 6.3.1).
Envelope times are RFC 3339 date-times in UTC and are not facet values.
1.4.5a Time of day #
A time of day with no date, on a 24-hour clock, at minute (14:30) or second (14:30:15) precision. When a
zone matters, it is a qualifier holding an IANA time zone name: 22:30 [zone: America/Chicago]. Two times
of day compare as clock times, and only when their zones are the same or both absent. How long something
lasts is not a time of day; it is a quantity (90min, chapter 1.4.3).
1.4.6 Yes-no #
Two values, yes and no. A facet may give them other words (Sealed, Opened); the stored value is still
yes or no.
1.4.7 Choice #
One value from the facet's list, stored as the list's canonical spelling, compared exactly. A value the list doesn't hold is new (vocabulary growth) unless the list is fixed (chapter 9).
1.4.8 Link #
A reference to another thing. Three forms:
| Form | Points at |
|---|---|
P-4QX7 |
a record of the same owner |
prefix:ID (me:P-4QX7, wd:Q311319) |
another owner's record, or a shared vocabulary such as Wikidata |
an absolute URL (https://…) |
a web page |
A link is not a copy: the target's values are read through it, never stored beside it.
1.4.9 Image #
A picture, by its location (an absolute URL or a package-defined content address), with optional
sha256 (lowercase hex of the bytes), w and h (pixels) and type (a media type). Two images are the
same image when their sha256 match.
1.4.10 Raw #
The value of an unknown key: the exact text as written (text form) or the exact JSON value (JSON form). A Reader MUST keep it unchanged and MUST NOT guess its type.
1.4.11 Compound #
A value made of pairs: each pair is a child facet and its value, and children may be compounds themselves, to
any depth. A compound's children are real facets, addressed through their parent (cast.character).
1.5 The three empties #
| State | Means | Satisfies "required"? |
|---|---|---|
| absent | the key isn't there; nobody has said anything | no |
| unknown | it applies, and the value is still owed | no |
| does not apply | no value, by design | yes, where a rule allows it |
The empties are data. There is no null value and no nullable flag: whether a facet may be unknown or not apply is decided by rules (chapter 6). An empty is always the whole value of a key; it MUST NOT appear as a list item or a qualifier.
1.6 Many values, rows and parts #
How many. A facet holds one value, up to N, or any number (chapter 4). A many-valued facet's value is a list, even when it holds one item. An empty list is the same as absent, and writers MUST NOT write one.
Ordered list or set. The facet says which. In an ordered list the order means something (billing order) and is kept. A set has no order; canonical forms sort it (chapter 3). Neither may hold the same item twice.
Rows. Each item of a many-valued compound is a row with a row key: a short name, unique within the list, that never changes when rows are reordered.
- If the compound declares an identity child (cast's
person), the row key is that child's link target (wd:Q310394), so two sources can tell they mean the same row. An identity child MUST be a link facet, and its row key is never written separately. - Otherwise the first writer mints a row key, and it MUST NOT be reused for another row of that list. A
compound or part slot without an identity child MAY declare a match child (
match: name): incoming rows are matched to existing ones by that child's fold key (chapter 4.3) before any new row key is minted (chapter 9.2). - A minted row key follows the ID grammar (1.1); a derived one is an ID or
prefix:ID.
Parts. Owned parts (disc 2 of a set, the case) are rows of the record's parts. Each has a row key, its
own wears and its own values. Shared parts are not parts: they are records of their own, linked by a link
facet, and read through.
Units mirror parts. A record at a lower level holds parts with the same row keys as the record above, carrying only the values that vary at its level (1.7).
Notables. Every record MAY hold crubal:notables, a core compound (key text, value text, any number,
rows matched by key) for facts that are true of this record and that no class asks for: signed, misprint,
edition number 4522, graded. Notables are data like any other, with sources. A notable key that recurs across
enough records is proposed as a real facet through review (chapter 9.5); a one-off stays a notable.
1.7 Values down the levels #
A record reads the values of the records above it. A lower record stores a value only for a facet the package declares to vary at that level step; for any other facet it MUST NOT store one, and readers see the upper record's value. A stored value at the lower level overrides the upper one for that record only.
A value is stored once, at the highest level where it is true.
1.8 Qualifiers #
A qualifier is detail about one value that is part of what it means: PG-13 in the US, 15 in the
UK. Each qualifier is a key and a value, where the key is a facet (so it has a type) and the value is not
itself qualified. A qualified value equals another only when the value and every qualifier are equal.
Four names are reserved inside a value's detail and MUST NOT be used as qualifier keys: row, origin,
via and owner.
1.9 Sources #
Every stored value carries a source of three fields.
| Field | Holds | Changes? |
|---|---|---|
origin |
how the value was first established: observed (read off a real instance), declared (its maker said so), derived (a rule computed it), reported (a catalog says so, origin unknown) |
never |
via |
the log entry of the write that put it here (chapter 8) | on every write |
owner |
who controls the value now | by adopting |
- A source attaches to an address (1.10): a whole value, one list item, or one compound leaf.
- A record has a default source; a source at a more specific address wins.
owneris omitted when it is the record's owner.- Values read through from an upper level or a shared part carry no source on the lower record. Their source lives where they are stored.
- One write makes one log entry, however many values it touches.
1.10 Addresses #
An address names one place inside a record. The same addresses are used by sources, intake, observations and rules.
address = step *( "." step )
step = key [ "[" selector "]" ]
selector = rowkey / quoted-item
| Address | Names |
|---|---|
runtime |
the value of runtime |
cast[wd:Q229048].character |
character in the row of cast whose identity is wd:Q229048 |
tracks[t3].language |
language in the row of tracks with the minted key t3 |
parts[disc1].condition |
condition on owned part disc1 |
genres["Holiday"] |
the item Holiday of the list genres |
unit::condition |
condition at the unit level, written from above (rules and classes only) |
A list item of a plain list is addressed by its value: the item's text form (chapter 2), with its
qualifiers but not its sources, written as a quoted text: genres["Holiday"], rating["15 [region: UK]"]. A selector that matches nothing addresses nothing; it is
not an error to read, and it is an error to write a source to.
1.11 Equality and identity #
Two values are equal when they have the same shape and, by shape: text and choice exactly; numbers, quantity
amounts and money amounts by numeric value, with the same unit or currency; dates with the same precision and
fields, and moments by the instant they name; times of day by clock time and zone; links with the same target string; images by sha256 when both have one, otherwise by location;
compounds pair by pair; lists item by item, in order for an ordered list and as sets otherwise. Qualifiers
take part in equality. Sources never do.