Hyperedges, per-file storage, and definition types
Glossarist v3.3 ships GenericHyperedge (the genus/species rake parallel to PartitiveHyperedge), promotes n-ary relations to first-class files at relations/<id>/<slug>.yaml, and adds ISO 704:2022 §5.3 definition types. The clean break drops backward compatibility for bundled partitive_relations arrays.
Today we’re shipping three changes that finish the ISO 704:2022 rake story for Glossarist:
GenericHyperedge— the genus/species n-ary rake, structural mirror ofPartitiveHyperedge- Per-file n-ary relation storage — every Partitive / Generic hyperedge
now lives at
relations/<comprehensive-id>/<criterion-slug>.yaml - ISO 704:2022 §5.3 definition types —
intensional,extensional,partitive,translatedonDetailedDefinition
This is a clean break. We’ve dropped backward compatibility for the
bundled partitive_relations: arrays on concept files. The wire format is
per-file only. A migration script handles existing data.
What a hyperedge looks like
ISO 704:2022 rake diagrams encode member semantics in the line notation itself. A solid line means a part is required; a dashed line means optional; a bold 3x-width line means delimiting. The horizontal backline indicates whether the decomposition is complete (joined) or partial (continues off-page).
Here’s a PartitiveHyperedge — the VIM measurement result (2.9) rake:
Three teeth drop from a joined backline: one solid (required part), one bold 3x (required + delimiting — distinguishes from coordinate concepts), and one dashed (optional). All three share the comprehensive AND the criterion of subdivision, making them coordinate concepts per ISO 12620.
1. GenericHyperedge
The ISO 704:2022 MECE model — presence × count as orthogonal dimensions,
plus the bold-line delimiting flag — applies to both partitive and
generalization (genus/species) rakes. PartitiveHyperedge ships the
partitive side; GenericHyperedge ships the genus/species side.
The trigger for adding GenericHyperedge was concrete: OIML V 2-200:2010 contains ~8 generic hyperedges, several with 6 criterion-of-subdivision groups per comprehensive. Measurement standard (5.1) decomposes by realization medium, by calibration role, by governance level, by metrological hierarchy, by reference/working, and by travel — six distinct rakes on the same genus.
Here’s how that looks in source diagrams:
Binary broader_generic edges can’t express this grouping coherently. You’d
have six flat lists of narrower edges with no way to distinguish which
species belong to which decomposition. GenericHyperedge groups species by
criterion.
GenericHyperedge solves this with the same shape as PartitiveHyperedge:
GenericHyperedge extends AbstractHyperedge
+comprehensive: ConceptRef [1] # genus concept
+members: GenericMember [2..*] # species concepts
+completeness: Completeness [0..1] # complete | partial
+criterion: LocalizedString [0..1] # subdivision principle
+sources: ConceptSource [0..*] # bibliographic provenance
+notes: LocalizedString [0..1] # optional commentary
+status: ConceptStatus [0..1] # lifecycle (independent)
GenericMember extends HyperedgeMember
+ref: ConceptRef [1]
+presence: Presence [0..1] # required | optional
+count: Count [0..1] # exactly_one | at_least_one | multiple
+is_delimiting: Boolean [0..1] # ISO 704 bold 3x-width line
Two GenericHyperedges on the same comprehensive, distinguished by criterion, are distinct decompositions — not duplicates.
OCP-compliant type dispatch
Both leaves inherit from a shared AbstractHyperedge base, and members
inherit from HyperedgeMember. A HyperedgeRegistry (JS) / autoload-backed
constant (Ruby) maps the wire type discriminator (partitive_relation /
generic_relation) to the correct class at load time.
Adding a fourth n-ary type later — e.g. SequentialHyperedge for ISO 12620
sequential concept systems — is one class addition plus a registry entry.
No validator or schema refactor.
ExternalConcept as comprehensive
Two of OIML’s generalization rakes have parenthetical/external comprehensives — “(quantity expressed by a measurement unit)” and “(precision condition of measurement)”. These are ISO 704’s “parenthetic terms”: concepts taken as primitives, assumed understood.
Glossarist handles this: an ExternalConcept (status: external) can serve
as the comprehensive of a GenericHyperedge. The hyperedge file lives in the
ExternalConcept’s owning dataset; the ExternalConcept’s provided_by edge
resolves the actual definition from elsewhere.
A new validator — check-external-as-comprehensive — warns when such an
ExternalConcept has no provided_by edge, since the decomposition dangles
otherwise.
2. Per-file relation storage
This is the architectural change with the biggest impact on dataset layout.
Until now, partitive_relations: arrays lived inside concept YAML files.
That worked when the array was small. With OIML-scale data it breaks:
- i18n bloat — a concept like measurement standard has 6 generic rakes, each carrying localized criterion + notes + sources’ modification text. ~60+ localized strings piled into the concept file alongside its own localized_concepts.
- Editing conflicts — translator working on French definition vs terminologist restructuring the rake both touch the same YAML.
- Provenance — every hyperedge should carry its own sources; that bulk doesn’t belong on the concept.
- Lifecycle independence — a concept can be
validwhile one of its decompositions isdraft. If they share a file, the file’s status is ambiguous.
The new layout
relations/<id>/.Each file holds exactly one hyperedge (one comprehensive + one criterion
- its members). The directory IS the index —
relations/5-1/enumerates every decomposition of concept 5.1.
Why inline i18n (not per-language sub-files)
Hyperedge text is short — criterion is a phrase, notes are 1-2 sentences.
Inline LocalizedString (object keyed by ISO 639 code) is the right
granularity. Per-language sub-files would 4x the file count for marginal
translator benefit.
This matches what PartitiveHyperedge.criterion already does — extend the
pattern to notes and any other text fields.
What stays bundled
Binary related edges stay in the concept file. The argument for
per-file doesn’t apply with the same force:
- A binary edge has at most one
content(localized text), usually short - Edges are structurally part of the concept’s neighborhood
- Splitting would 10x the file count for concepts with many
relatedentries
The dividing line is “n-ary with criterion.” Anything that crosses it becomes a file. Binary stays inline.
Member multiplicity — MECE decomposition
ISO 704:2022 encodes per-member multiplicity via two orthogonal diagram line dimensions. Glossarist decomposes this into a MECE pair: presence (solid vs dashed) × count (line count).
Five valid combinations, one invalid. The invalid (optional, at_least_one)
combo is rejected: an optional part has vacuous at-least-one (it may not
exist at all), so it collapses to (optional, multiple). Both the JSON
Schema and the Ruby/JS validators reject it with a clear error message.
The third orthogonal flag — is_delimiting — encodes the bold 3x-width
line. It applies regardless of presence/count.
3. Definition types (ISO 704:2022 §5.3)
ISO 704 distinguishes four definition strategies. We’ve added type to
DetailedDefinition:
definition:
- type: intensional
content: "A measurement unit is a real scalar quantity..."
- type: partitive
content: "A measurement standard is composed of a reference
material, a measuring system, or a material measure."
Values: intensional (default), extensional, partitive, translated.
Additive change. When type is omitted, the definition is treated as
intensional — no migration needed for existing data.
The clean break
We are not preserving backward compatibility for the bundled
partitive_relations: array format. The clean break simplified the
implementation considerably:
- No “two readers, one writer” plumbing in glossarist-ruby / glossarist-js
- No long-tale support burden
- Schema is simpler —
concept.yamlhas no relation-shaped$defs - Validators don’t need a “bundled or per-file?” dispatch
The only adopters today are internal (glossarist-ruby, glossarist-js, concept-browser, vocab datasets) — all are updated in lockstep with this release. External adopters, if any emerge, get the new format from day one.
Migration
The migrate-to-per-file-relations script in concept-model/exe/ handles
existing data:
bundle exec exe/migrate-to-per-file-relations --backup path/to/concepts/
For each concept file with partitive_relations, the script:
- Extracts each hyperedge
- Derives a slug from the criterion (or “decomposition” if absent)
- Writes it to
relations/<comprehensive-id>/<slug>.yaml - Removes the
partitive_relationsarray from the concept file
The --dry-run flag previews; the --backup flag preserves .bak files.
Lockstep rollout
- concept-model — schemas, LUTAML, ontology, SHACL, JSON-LD, validators, migration script, examples
- glossarist-ruby —
V3::GenericHyperedge,V3::GenericMember,V3::HyperedgeMember(abstract base),V3::AbstractHyperedge(abstract base),V3::RelationLoader,V3::DefinitionType.V3::PartitiveHyperedgerefactored to inherit fromAbstractHyperedge. - glossarist-js — mirror classes:
GenericHyperedge,GenericMember,HyperedgeMember,RelationLoader,DefinitionType, plus aHyperedgeRegistryfor OCP-compliant type dispatch - concept-browser —
GenericRelationList.vue(mirrorsPartitiveRelationList.vuewith type label swapped); adapter types extended - vocab datasets — migration script runs across VIM 2007/2010/2012 + OIML V 2-200:2010
What we deliberately did not do
Three architectural non-decisions worth flagging:
- No
ConceptSystementity. ISO 704 §5.4 describes concept systems as the substrate. We already model that viadomains+tags+ the relation graph — adding a fourth entity duplicates existing structure. - No computed inheritance walk. ISO 704 §6.4’s inheritance principle is a reasoning pattern over correct intensional definitions, not a data structure. Adding one would duplicate data already in definitions.
- No
AssociativeHyperedgen-ary. No evidence of associative rakes in our datasets yet. Binaryrelatedcovers observed cases. Trigger to revisit: when a real dataset shows associative rakes with role-bearing members.
See docs/design/iso704-2022-mapping.md for the full reasoning.
The optomechanical mouse (ISO 704:2022 §5.5.4.2.2)
ISO 704:2022 uses optomechanical mouse as its canonical worked example for partitive decomposition. Here it is with all MECE dimensions exercised:
# relations/example-optomechanical-mouse/by-physical-structure.yaml
$id: example-optomechanical-mouse/by-physical-structure
type: partitive_relation
comprehensive: { source: EXAMPLE, id: "optomechanical-mouse" }
members:
- ref: { source: EXAMPLE, id: "mouse-ball" }
is_delimiting: true
- ref: { source: EXAMPLE, id: "x-axis-roller" }
is_delimiting: true
- ref: { source: EXAMPLE, id: "y-axis-roller" }
is_delimiting: true
- ref: { source: EXAMPLE, id: "infrared-emitter" }
is_delimiting: true
- ref: { source: EXAMPLE, id: "infrared-sensor" }
is_delimiting: true
- ref: { source: EXAMPLE, id: "circuit-board" }
# not delimiting — all computer mice have circuit boards
- ref: { source: EXAMPLE, id: "mouse-button" }
# not delimiting — all computer mice have buttons
- ref: { source: EXAMPLE, id: "mouse-wheel" }
presence: optional
# not all optomechanical mice have a wheel
completeness: complete
criterion: { eng: physical structure }
The five delimiting parts distinguish optomechanical mouse from coordinate concepts like mechanical mouse (no infrared emitter/sensor) and optical mouse (no ball, no rollers). Mouse button and wheel are non-delimiting — they don’t distinguish the comprehensive from its coordinates.
Design history
This release consolidates four iterations of n-ary relation modeling, each driven by real dataset evidence:
| Date | Iteration | What changed |
|---|---|---|
| 2026-07-22 | PartitiveHyperedge v1 | Initial model — enumeration: closed|open + opaque markers: [double, dashed] |
| 2026-07-23 | PartitiveRelation v2 | ISO 704 / 1087-1 / 12620 terminology; markers promoted to structured data; per-member certainty; criterion field; ExternalConcept support |
| 2026-07-29 | ISO 704:2022 MECE | Flat multiplicity enum replaced by MECE pair presence × count; delimiting parts as third orthogonal dimension |
| 2026-07-30 | Hyperedges + per-file + DefinitionType | (this release) GenericHyperedge parallel to PartitiveHyperedge; both inherit AbstractHyperedge / HyperedgeMember; per-file storage; ISO 704 §5.3 definition types |
Each iteration is preserved as a tag in the concept-model repository. The current wire format and class names are the ones described in this post; prior formats are reachable via git history if you need to understand the evolutionary pressure that shaped the current design.
What’s next
- OIML V 2-200:2010 migration — the ~8 generic rakes move to per-file format with criterion-of-subdivision groups preserved.
- VIM variants — VIM 2007/2010/2012 partitive relations already migrated; no additional work needed.
- SequentialHyperedge — ISO 12620 sequential concept systems (spatial/ temporal rakes) modeled by analogy. Trigger: when a real dataset needs n-ary spatial/temporal grouping AND Partitive/Generic have been stable for one release cycle.
Further reading
- Hyperedges — abstract base model (with SVG diagrams)
- Partitive Relations — whole/part specialization
- Generic Relations — genus/species specialization
- Relationships — binary typed edges
- Concepts — ManagedConcept, LocalizedConcept, ExternalConcept
- ISO 704 — Terminology work: Principles and methods
- ISO 12620 — Terminology and other content resources