4. Facets
A facet says what a key means: its type, how many values it holds, its choices, its unit, its other names and its version. A facet's definition is pairs about a key, written in the long form (4.2), in one line of shorthand (4.5), or as JSON (4.6). All three hold the same settings.
A facet never says what is true of a record, what a kind of thing asks for, or what must hold. Those are data (chapter 1), classes (chapter 5) and rules (chapter 6). Some settings are shortcuts for rules (4.4): they are written on the facet because nearly every facet needs them, and they expand to rules with one engine behind them.
4.1 Identity and versions #
- The key is the identity and never changes. It is written with its owner's prefix (
me:videoCodec); inside the owner's own files the bare key is enough. A rename changes thelabel, and the old spelling becomes a key alias (4.3). - Versions are dated:
YYYY-MM-DD, with.2,.3… for a second or later version published the same day (2026-10-13.2). A published version MUST NOT change. Each facet is versioned on its own; a package version is a frozen set of member versions (chapter 11). - A facet MAY carry a short ID (
F-7KQ2) for display or tooling. It is never the identity. - Lifecycle. A facet is
candidate,activeorretired(state:, defaultactive). A candidate is proposed vocabulary: it is stored and reviewable, but forms, views and rules don't see it, and class lines can't name it. A review decision promotes it to active, or merges it into an existing facet (its key and aliases become that facet's aliases). A retired facet keeps its stored values readable but is never asked for again; retiring is breaking (4.8). Classes have the same lifecycle (chapter 5.1). - Stored values carry no version. A breaking change (4.8) ships a migration recipe (chapter 10).
4.2 The long form #
facet: videoCodec
label: Video codec
ask: How is the picture encoded on the disc?
describe: The codec of the main feature, as read from the disc or its case.
type: choice
values: MPEG-2, H.264, VC-1 ~vc1, HEVC, AV1
aliases: codec, "Video Format"
same-as: schema:encodingFormat
version: 2026-10-13
A definition is a block under facet: key. Its settings:
Every type #
| Setting | Holds | Default |
|---|---|---|
type |
text, number, date, time, yes-no, choice, link, image or compound |
required |
label |
the name people see | the key, split at capitals |
ask |
the short question beside the control | none |
describe |
what the facet means: where it comes from, what it drives | none |
pick |
how many values: 1, a number N for "up to N", or any |
1 |
ordered |
many-valued only: yes makes the list an ordered list; otherwise it is a set (chapter 1.6) |
no |
qualifiers |
the facets whose values may qualify this facet's values (chapter 1.8) | none |
aliases |
other spellings of the key (4.3) | none |
same-as |
the same property elsewhere: schema:name, wd:P577 |
none |
state |
candidate, active or retired (4.1) |
active |
agree-by |
how two values compare for agreement and conflict (chapter 9.3): equal, fold (the alias fold, 4.3), or a package-named normalizer (me:titleNoise, which may drop leading articles and format words). Storage still holds one real value |
equal, or fold for text and choice |
version |
the dated version (4.1) | required when published |
By type #
| Type | Setting | Holds |
|---|---|---|
| text | length |
the most characters (a shortcut, 4.4) |
| number | unit |
a unit spelling (min, lb), an ISO 4217 code (USD), or currency for money in any currency |
| number | accepts |
other units a value may arrive in; they are converted to unit by the units table on the way in, with the rounding the table states |
| number | range |
1..600, optionally step 0.5 (a shortcut, 4.4) |
| number | places |
the most decimal places kept |
| date | precision |
year, month, day, hour, minute, second, or any (all accepted) |
| date | zone |
local when the date is wall-clock time with no offset (chapter 1.4.5) |
| time | precision |
minute (the default) or second |
| yes-no | words |
the two words when they aren't yes and no: Sealed, Opened |
| choice | values |
the list, in order, each with ~ spellings (4.3); ends in ... when people may add to it (4.2.1) |
| choice | fixed |
yes when the list is fixed by definition (4.2.1) |
| choice | ranked |
yes when the list's order is a ranking (NM > VG > … > P), so best, worst, over and under mean something |
| choice | scored |
yes when each pick also takes a grade from 0 to 10, stored as the qualifier grade |
| link | to |
what it points at: a kind, a class, url, or a shared vocabulary's prefix (wd) |
| compound | facets |
the child facets: blocks or shorthand lines (4.5) |
| compound | identity |
the child that identifies a row; it MUST be a link child (chapter 1.6) |
Shortcuts for rules #
required, na, unique, when, length, range and combine (4.4).
Package settings #
A setting key with a prefix (me:scanRank) belongs to that package. A reader that doesn't know it MUST
carry it unchanged and MUST NOT let it change what the facet means. Machinery (which service fills a facet,
how a scanner ranks it) lives here, never in the core settings.
4.2.1 Open, closed and fixed lists #
| List | Written | A person in a form | A value from intake |
|---|---|---|---|
| open | values ends in ... |
may add a value ("+ Other…"); it is new | lands as new, for vocabulary review |
| closed | no ..., no fixed |
picks from the list | lands as new, for vocabulary review |
| fixed | fixed: yes |
picks from the list | is a contradiction with the definition |
A list is fixed only when the world fixes it (Blu-ray regions A, B, C). A closed list is just the list we
have so far. ... and fixed: yes MUST NOT both be set.
4.3 Aliases #
Aliases are naming, not rules. They mean the same thing always, everywhere, for that facet.
- Key aliases (
aliases: codec) are other spellings of the key. - Value aliases (
VC-1 ~vc1 ~"VC 1") are other spellings of one choice value.
Folding. Aliases are folded before any rule runs. Stored data never holds an alias, and printers write
only the real name. Matching compares fold keys: the spelling in NFC, with Unicode default case folding,
leading and trailing spaces removed, and every run of white space made one space. The real key and the real
value match under the same folding, so dvd folds to DVD without an alias.
What isn't an alias. If it depends on context, it is a suggest rule. If it is another system's
vocabulary, it is a contract's value map (->). If it is the same thing under different facets, it is a
shared link (&wd:…).
Dictionary rules (checked when a package is published, 4.7):
- a key alias MUST NOT fold to the same key as another facet's key or key alias in the package;
- a value alias MUST NOT fold to the same key as another value or value alias of the same facet;
- two values of one facet MUST NOT fold to the same key.
Changes. Adding an alias is safe. Removing one is safe for stored data but can break imports, so it is a minor change. Turning a value into an alias of another is a merge, which is breaking (4.8).
4.4 Shortcuts for rules #
Each shortcut expands to one rule (chapter 6), with the facet as its subject. The expanded rule is the truth; the setting is a short way to write it.
| Setting | Expands to | Strength |
|---|---|---|
required: yes |
then: key has |
must |
na: yes |
- is an allowed value for key |
must (without it, - breaks a must rule) |
unique: yes |
then: key is unique (a global rule) |
must |
when: condition |
outside the condition, key is - |
must |
length: 80 |
when: key has then: key under 81 characters |
should |
range: 1..600 |
when: key has then: key in 1..600 |
should |
combine: worst |
then: key is worst of parts[*].key (chapter 6's rollups) |
must |
A rule written in full and a shortcut that say the same thing are the same rule; a package MUST NOT carry both.
Combine rules: join, sum, best, narrow, worst, deduct, replace, keep. The default is
join for many-valued facets and replace for single ones. best and worst need a ranked choice, a
number or a date. Only replace depends on the order contributions arrive in.
4.5 Shorthand #
One facet on one line, for the common case. It always expands to the long form.
!title$200 ~name =schema:name
videoCodec{MPEG-2|H.264|VC-1~vc1|HEVC|AV1} ~codec =schema:encodingFormat /2026-10-13
productionFormat{Full Length Movie|Shorts|...}
releaseDate@any
runtime#min 1..600
price#USD
sealed?
condition{NM|VG|G|F|P} ranked
cast(person&Person character$)* identity: person
genres{Comedy|Holiday|Drama|...}*
Read left to right:
line = [ "!" ] key type [ count ] *( " " tail )
type = "$" [ digits ] ; text, optional length
/ "#" [ unit ] ; number, optional unit or currency
/ "@" ( "y" / "ym" / "ymd" / "ymdh" / "ymdhm" / "ymdhms" / "any" ) ; date, its precision
/ "@" ( "hm" / "hms" ) ; time of day, its precision
/ "?" ; yes-no
/ "{" choice *( "|" choice ) [ "|..." ] "}" ; choice, optionally open
/ "&" target ; link
/ "^" ; image
/ "(" line *( " " line ) ")" ; compound
choice = value *( "~" value ) ; a value and its spellings
count = "*" [ digits ] ; any, or up to N
tail = "~" key ; key alias
/ "=" key ; same as
/ "+" combine ; combine rule
/ "/" version ; version
/ number ".." number ; range
/ setting ; a bare word or "name: value" from 4.2
!isrequired: yes.@alone means@any. The letters after@always run y, m, d, h, m, s, so the secondmis minutes:@ymdhmis a date with a time to the minute,@hma time of day.- A choice value holding
|,~,{,},(,)or a space at either end is quoted with chapter 2's quoting:{"A|B"|C}. - Inside a compound's parentheses, children are separated by spaces and are themselves shorthand lines.
- Settings with no mark (
label,ask,describe,qualifiers,accepts,places,words,ranked,scored,fixed,na,unique,when,ordered,identity, package settings) go on the line as bare tails: a word alone meansyes(ranked), andname: valuegives a value (identity: person). A list value runs on after each comma (accepts: g, lb, oz); any other value with a space is quoted. - The level mark
unit::belongs to class lines (chapter 5), not to a facet's own definition. Where a facet varies is declared by kinds and classes, never by the facet.
A printer SHOULD write the shorthand when every setting fits on one line within 100 characters, and the long form otherwise. It MUST NOT drop a setting to make a line fit.
4.6 The JSON form of a definition #
The long form's pairs as one object. Member names are the setting names; values follow chapter 3, with
yes/no as true/false and numbers as strings.
{
"facet": "videoCodec",
"type": "choice",
"label": "Video codec",
"ask": "How is the picture encoded on the disc?",
"values": [
{ "value": "MPEG-2" }, { "value": "H.264" }, { "value": "VC-1", "aliases": ["vc1"] },
{ "value": "HEVC" }, { "value": "AV1" }
],
"aliases": ["codec", "Video Format"],
"same-as": ["schema:encodingFormat"],
"version": "2026-10-13"
}
valuesis an array of{"value", "aliases"?}; an open list adds"open": trueat the top level.facets(compound) is an array of child definitions, each with its ownfacetkey.rangeis{"min": "1", "max": "600", "step"?: "0.5"}.- Settings equal to their default are left out. Package settings keep their prefix as the member name.
- A shortcut is stored as its setting. The expansion (4.4) happens when rules are built, never in storage.
4.7 Well-formed definitions #
A package MUST be refused at publish when any definition breaks these:
typeis one of the nine types, and every setting is one its type allows (4.2).- A choice facet has at least one value. A compound has at least one child.
identitynames a child of the compound, and that child is a link.orderedis set only whenpickis not1.ranked,scored,fixedand...are set only on choice facets;fixedand...not both.unitandacceptsare spellings from the units table or ISO 4217 codes, andacceptsis set only withunit.- Every
qualifiersentry names a facet whose own values can't be qualified. - No alias collides (4.3).
rangehasmin≤max, andstep, when set, divides the span.- A compound's children have distinct keys, and none is named
row,origin,viaorowner.
4.8 Changes and versions #
| Change | Kind | Needs a recipe? |
|---|---|---|
label, ask, describe, same-as |
safe | no |
| add a key alias or value alias, add a value | safe | no |
| remove an alias | minor (imports may break) | no |
open a closed list (...), widen pick, widen range or length |
safe | no |
| remove a value, merge values, rename a value | breaking | rename · merge |
narrow pick, precision or places; change unit |
breaking | review · convert |
change type |
breaking | merge + review |
fix a list, add required, add a must rule |
breaking for data that breaks it | review |
| retire a facet | breaking (it stops being asked) | review or move |
| promote a candidate | safe | no |
change combine |
breaking for rollups | recompute |
A breaking version MUST ship a recipe (chapter 10). A store applies it before it accepts data written under the new version.
4.9 Checking a value against its facet #
At the Dictionary level, after aliases fold, each value is checked against its facet. The result is one of chapter 9's intake outcomes, and the checks are:
| Check | Fails as |
|---|---|
| the value has the facet's shape (a date where a date is wanted) | contradiction, citing type |
| a date has a precision the facet accepts; a more precise date is cut to what the facet keeps, and the intake record keeps the rest | flagged when less precise than every accepted precision (it still lands) |
a quantity's unit is unit or in accepts |
contradiction, citing type |
| a choice value is in the list | new for an open or closed list; contradiction, citing type, for a fixed list |
no more values than pick allows |
contradiction, citing type; review offers both fixes |
qualifiers are among qualifiers |
the unknown qualifier is kept and flagged |
length, range |
flagged (they are should rules) |
A value that fails is kept: on the intake record and beside the facet, never dropped.