diff --git a/docs/rules.md b/docs/rules.md
index 50ee93a..fb38cd4 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -933,6 +933,23 @@ See [this rule in the specification](spec/#OOLD-VER-edb9) (section: identificati
## EXT - Standard extensions (JSON-LD and JSON Schema)
+### OOLD-EXT-05d3
+
+- Level: MUST NOT
+- Applies to:implementation
+- Machine-checkable: no
+- Since: 1.0.0-rc.3
+
+A frame derivation must not emit a subframe for a property whose key aliases a JSON-LD keyword.
+
+A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)).
+
+??? quote "In context"
+
+ A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects.
+
+See [this rule in the specification](spec/#OOLD-EXT-05d3) (section: framing).
+
### OOLD-EXT-1dc8
- Level: MUST NOT
@@ -1211,6 +1228,23 @@ A tool that derives a frame MUST derive it mechanically: the schema's class type
See [this rule in the specification](spec/#OOLD-EXT-68fa) (section: framing).
+### OOLD-EXT-6d10
+
+- Level: MUST
+- Applies to:implementation
+- Machine-checkable: no
+- Since: 1.0.0-rc.3
+
+A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, or a term mapped @type @id.
+
+A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`.
+
+??? quote "In context"
+
+ A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects.
+
+See [this rule in the specification](spec/#OOLD-EXT-6d10) (section: framing).
+
### OOLD-EXT-6ea3
- Level: SHOULD
@@ -1249,6 +1283,23 @@ By default a converter co-emits only `skos:exactMatch` entries; entries whose `p
See [this rule in the specification](spec/#OOLD-EXT-7256) (section: synonyms).
+### OOLD-EXT-725f
+
+- Level: MUST
+- Applies to:implementation
+- Machine-checkable: no
+- Since: 1.0.0-rc.3
+
+Framing a graph that holds several nodes matching a derived frame yields one instance document per match.
+
+Framing a graph with a schema-derived frame MUST yield one instance document per matching node, delivered as a `@graph` of matches rather than an arbitrary single root.
+
+??? quote "In context"
+
+ Framing a graph with a schema-derived frame MUST yield one instance document per matching node, delivered as a `@graph` of matches rather than an arbitrary single root. Dually, several instance documents - each expanded through its own `$schema` - merge into one graph, because expansion assigns every node its own IRI and identical IRIs denote the same node.
+
+See [this rule in the specification](spec/#OOLD-EXT-725f) (section: framing-many-documents).
+
### OOLD-EXT-7c5d
- Level: MUST
@@ -1467,3 +1518,20 @@ A property coerced `"@type": "@vocab"` therefore SHOULD constrain its values wit
Because `@vocab` expands an unmatched string against the vocabulary - concatenating it onto the default vocabulary base when one is set (minting a new IRI), or leaving it a relative IRI when none is - a typo silently becomes a stray IRI rather than an error. A property coerced `"@type": "@vocab"` therefore SHOULD constrain its values with an `enum` of the value terms (optionally named with `x-enum-varnames`) or with `x-oold-range`, so only intended individuals are accepted. The value terms SHOULD also be kept from colliding with JSON-LD keyword aliases (`id`, `type`) or other context terms, since a value term shares the context's global term namespace - a term added for a value would otherwise also rewrite a property or keyword of the same name. Confining the value terms to the property's own scoped `@context` keeps them out of that shared namespace, since they then resolve only for that property's values; naming them with opaque identifiers such as UUIDs avoids the clash where readability is not required.
See [this rule in the specification](spec/#OOLD-EXT-fdd8) (section: value-term-aliases).
+
+### OOLD-EXT-ff64
+
+- Level: MUST
+- Applies to:implementation
+- Machine-checkable: no
+- Since: 1.0.0-rc.3
+
+Where a property matches both the embedded-object and the reference-valued signals, the embedded-object reading wins.
+
+Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares.
+
+??? quote "In context"
+
+ A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](spec/#range-of-properties) on a string-typed value, an IRI-family `format` (the family [range-reference-form](spec/#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [identity](spec/#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects.
+
+See [this rule in the specification](spec/#OOLD-EXT-ff64) (section: framing).
diff --git a/docs/spec/index.html b/docs/spec/index.html
index b044b09..b345417 100644
--- a/docs/spec/index.html
+++ b/docs/spec/index.html
@@ -912,6 +912,8 @@
Terminology
Framing
JSON-LD 1.1 Framing reshapes a flat or arbitrarily-structured RDF graph into a specific tree layout described by a frame. An OO-LD schema already describes exactly such a tree - its properties give the nesting, its @context gives the term IRIs, a type constant (x-oold-instance-rdf-type, or a const on the type property) gives the node type, and x-oold-range gives the type of embedded or referenced objects - so an OO-LD-aware tool MAY auto-construct a frame from the schema. Framing an instance graph with that frame produces a JSON document shaped like the schema, which then validates against the same schema.
This makes an OO-LD schema bidirectional: its @context drives expansion (JSON to RDF), and the frame derived from its structure drives framing (RDF back to the schema's JSON tree). A tool that derives a frame MUST derive it mechanically: the schema's class type becomes the frame @type; an inlined object property becomes a nested subframe that embeds the referenced node; a reference-valued property (including one whose term is mapped with JSON-LD @reverse) becomes a subframe with @embed: @never so its targets stay IRIs, in line with the inline-versus-reference choice x-oold-range already records; and @explicit / @requireAll / @default follow from additionalProperties and required. The frame's @context is the composition of the referenced schemas' contexts.
+
A derivation MUST recognize a property as reference-valued from any of three signals: an x-oold-range on a string-typed value, an IRI-family format (the family recommends), or a @context term mapped "@type": "@id".Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares.A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally id for @id and type for @type (see ). Such a key names the node rather than pointing at another one, and a subframe under it produces {"@id": {...}}, which a processor rejects.
+
Omitting @embed: @never is not a cosmetic difference. A reference whose target happens to carry triples in the same graph is then embedded as an object, and the framed document no longer validates against the schema the frame was derived from, which declares a string in that position.
-
Framing is not the only option for this transformation. When the source data lives in a triplestore or behind a SPARQL endpoint, a SPARQLCONSTRUCT query - also derivable from the schema - can select and reshape the relevant subgraph directly, including deriving reverse relations such as employees from the inverse of schema:worksFor; compacting that result with the schema's @context (and framing it where nesting is required) yields the same OO-LD instance.
JSON Schema
OO-LD targets [[JSONSCHEMA]] (2020-12) as its normative dialect. An OO-LD schema SHOULD declare the OO-LD dialect meta-schema (which extends 2020-12) as its $schema, e.g. "$schema": "https://oo-ld.org/latest/meta/oold-meta-schema.json" - pinning a specific version (e.g. .../0.4.0/meta/oold-meta-schema.json) for reproducibility. Declaring the plain 2020-12 meta-schema (https://json-schema.org/draft/2020-12/schema) remains valid for tools that only understand standard JSON Schema.
+
Many documents, one graph
The example above frames a graph that happens to hold one matching node. In general a graph holds many, and the relationship runs in both directions.
+
Framing a graph with a schema-derived frame MUST yield one instance document per matching node, delivered as a @graph of matches rather than an arbitrary single root. Dually, several instance documents - each expanded through its own $schema - merge into one graph, because expansion assigns every node its own IRI and identical IRIs denote the same node.
+
The @embed: @never derivation is what keeps the two directions consistent. A reference that stays a reference belongs to exactly one document: the one describing the node it is stored on. If framing embedded it instead, the target's triples would be copied into every document that references it, so a node's description would depend on which document a reader happened to open, and merging the documents back would re-assert those triples from several places at once. Keeping references as IRIs means each document round-trips independently and their union is the graph they came from.
+
This is the framing-based answer to the object-graph mapping question: a schema does not describe a document in isolation, it describes how to cut one document out of a graph and how to put it back.
+
Framing is not the only option for this transformation. When the source data lives in a triplestore or behind a SPARQL endpoint, a SPARQLCONSTRUCT query - also derivable from the schema - can select and reshape the relevant subgraph directly, including deriving reverse relations such as employees from the inverse of schema:worksFor; compacting that result with the schema's @context (and framing it where nesting is required) yields the same OO-LD instance.
JSON Schema
OO-LD targets [[JSONSCHEMA]] (2020-12) as its normative dialect. An OO-LD schema SHOULD declare the OO-LD dialect meta-schema (which extends 2020-12) as its $schema, e.g. "$schema": "https://oo-ld.org/latest/meta/oold-meta-schema.json" - pinning a specific version (e.g. .../0.4.0/meta/oold-meta-schema.json) for reproducibility. Declaring the plain 2020-12 meta-schema (https://json-schema.org/draft/2020-12/schema) remains valid for tools that only understand standard JSON Schema.
2020-12 is REQUIRED, not merely preferred: OO-LD's composition places $ref alongside sibling keywords (e.g. a property carrying type, x-oold-range and @context, or allOf: [{$ref: ...}] next to properties). Keywords adjacent to $ref are only evaluated from JSON Schema 2019-09 onward; in Draft 4 and Draft 7 they are ignored ([[JSONSCHEMA]] ยง8.2.3.1). Keywords such as const (used throughout this document) are likewise only available from draft-06 onward. Migration from the earlier Draft-4-style notation: rename definitions to $defs, id to $id, and use the numeric form of exclusiveMinimum/exclusiveMaximum instead of the boolean form.
Enum names and descriptions
An enum constrains a property to a fixed set of values. Those values are data, chosen for what they mean on the wire, so they are routinely not usable as identifiers in generated code: an IRI, a CURIE, a value carrying spaces or hyphens, or a number. Two established vendor extensions carry the missing information, each a list positionally aligned with enum:
x-enum-varnames - the identifier-safe name of each value, used as the generated enum member name.
diff --git a/meta/oold-rules.json b/meta/oold-rules.json
index 3399069..e07b940 100644
--- a/meta/oold-rules.json
+++ b/meta/oold-rules.json
@@ -212,6 +212,21 @@
"deprecated": false,
"source": "03-conformance.md:13"
},
+ {
+ "id": "OOLD-EXT-05d3",
+ "area": "EXT",
+ "level": "MUST NOT",
+ "applies_to": "implementation",
+ "section": "framing",
+ "summary": "A frame derivation must not emit a subframe for a property whose key aliases a JSON-LD keyword.",
+ "text": "A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)).",
+ "text_sha256": "07e05da8b2fcd62c4560330cca1372fbfe129432a17fa660040f417566c43dc9",
+ "context": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.",
+ "machine_checkable": false,
+ "since": "1.0.0-rc.3",
+ "deprecated": false,
+ "source": "09-extensions.md:98"
+ },
{
"id": "OOLD-EXT-1dc8",
"area": "EXT",
@@ -240,7 +255,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.2",
"deprecated": false,
- "source": "09-extensions.md:423"
+ "source": "09-extensions.md:437"
},
{
"id": "OOLD-EXT-1f92",
@@ -255,7 +270,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:343"
+ "source": "09-extensions.md:357"
},
{
"id": "OOLD-EXT-2542",
@@ -270,7 +285,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:352"
+ "source": "09-extensions.md:366"
},
{
"id": "OOLD-EXT-2b61",
@@ -285,7 +300,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:346"
+ "source": "09-extensions.md:360"
},
{
"id": "OOLD-EXT-391e",
@@ -300,7 +315,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:337"
+ "source": "09-extensions.md:351"
},
{
"id": "OOLD-EXT-3fe9",
@@ -315,7 +330,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:315"
+ "source": "09-extensions.md:329"
},
{
"id": "OOLD-EXT-436a",
@@ -330,7 +345,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:446"
+ "source": "09-extensions.md:460"
},
{
"id": "OOLD-EXT-44bd",
@@ -345,7 +360,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.3",
"deprecated": false,
- "source": "09-extensions.md:212"
+ "source": "09-extensions.md:226"
},
{
"id": "OOLD-EXT-4966",
@@ -375,7 +390,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:185"
+ "source": "09-extensions.md:199"
},
{
"id": "OOLD-EXT-557e",
@@ -405,7 +420,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:358"
+ "source": "09-extensions.md:372"
},
{
"id": "OOLD-EXT-61aa",
@@ -420,7 +435,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:445"
+ "source": "09-extensions.md:459"
},
{
"id": "OOLD-EXT-6312",
@@ -435,7 +450,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:227"
+ "source": "09-extensions.md:241"
},
{
"id": "OOLD-EXT-68fa",
@@ -452,6 +467,21 @@
"deprecated": false,
"source": "09-extensions.md:96"
},
+ {
+ "id": "OOLD-EXT-6d10",
+ "area": "EXT",
+ "level": "MUST",
+ "applies_to": "implementation",
+ "section": "framing",
+ "summary": "A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, or a term mapped @type @id.",
+ "text": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`.",
+ "text_sha256": "f7aef6a0c109e10d28813bf45fe0808edbb5efdfab22edac7a171cd13a743f4d",
+ "context": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.",
+ "machine_checkable": false,
+ "since": "1.0.0-rc.3",
+ "deprecated": false,
+ "source": "09-extensions.md:98"
+ },
{
"id": "OOLD-EXT-6ea3",
"area": "EXT",
@@ -465,7 +495,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:341"
+ "source": "09-extensions.md:355"
},
{
"id": "OOLD-EXT-7256",
@@ -482,6 +512,21 @@
"deprecated": false,
"source": "09-extensions.md:64"
},
+ {
+ "id": "OOLD-EXT-725f",
+ "area": "EXT",
+ "level": "MUST",
+ "applies_to": "implementation",
+ "section": "framing-many-documents",
+ "summary": "Framing a graph that holds several nodes matching a derived frame yields one instance document per match.",
+ "text": "Framing a graph with a schema-derived frame MUST yield one instance document per matching node, delivered as a `@graph` of matches rather than an arbitrary single root.",
+ "text_sha256": "ceb79118ae8684c9a82b93e11eb96ec7ebd9d2515dbc087ca1c5afecd1f6fb57",
+ "context": "Framing a graph with a schema-derived frame MUST yield one instance document per matching node, delivered as a `@graph` of matches rather than an arbitrary single root. Dually, several instance documents - each expanded through its own `$schema` - merge into one graph, because expansion assigns every node its own IRI and identical IRIs denote the same node.",
+ "machine_checkable": false,
+ "since": "1.0.0-rc.3",
+ "deprecated": false,
+ "source": "09-extensions.md:189"
+ },
{
"id": "OOLD-EXT-7c5d",
"area": "EXT",
@@ -540,7 +585,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:187"
+ "source": "09-extensions.md:201"
},
{
"id": "OOLD-EXT-b23b",
@@ -555,7 +600,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.2",
"deprecated": false,
- "source": "09-extensions.md:204"
+ "source": "09-extensions.md:218"
},
{
"id": "OOLD-EXT-b249",
@@ -570,7 +615,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.2",
"deprecated": false,
- "source": "09-extensions.md:200"
+ "source": "09-extensions.md:214"
},
{
"id": "OOLD-EXT-c77a",
@@ -585,7 +630,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.3",
"deprecated": false,
- "source": "09-extensions.md:288"
+ "source": "09-extensions.md:302"
},
{
"id": "OOLD-EXT-dd76",
@@ -600,7 +645,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:212"
+ "source": "09-extensions.md:226"
},
{
"id": "OOLD-EXT-ddda",
@@ -630,7 +675,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:337"
+ "source": "09-extensions.md:351"
},
{
"id": "OOLD-EXT-eeda",
@@ -645,7 +690,7 @@
"machine_checkable": false,
"since": "1.0.0-rc.3",
"deprecated": false,
- "source": "09-extensions.md:381"
+ "source": "09-extensions.md:395"
},
{
"id": "OOLD-EXT-ef09",
@@ -660,7 +705,7 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:212"
+ "source": "09-extensions.md:226"
},
{
"id": "OOLD-EXT-fdd8",
@@ -675,7 +720,22 @@
"machine_checkable": true,
"since": "1.0.0-rc.1",
"deprecated": false,
- "source": "09-extensions.md:352"
+ "source": "09-extensions.md:366"
+ },
+ {
+ "id": "OOLD-EXT-ff64",
+ "area": "EXT",
+ "level": "MUST",
+ "applies_to": "implementation",
+ "section": "framing",
+ "summary": "Where a property matches both the embedded-object and the reference-valued signals, the embedded-object reading wins.",
+ "text": "Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares.",
+ "text_sha256": "52ad7df4c37e1dc4253a1db04f928fc84099a704077d2b5c35d74807b72abf2c",
+ "context": "A derivation MUST recognize a property as reference-valued from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `\"@type\": \"@id\"`. Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{\"@id\": {...}}`, which a processor rejects.",
+ "machine_checkable": false,
+ "since": "1.0.0-rc.3",
+ "deprecated": false,
+ "source": "09-extensions.md:98"
},
{
"id": "OOLD-INS-1d33",
diff --git a/meta/rules-baseline.json b/meta/rules-baseline.json
index f85a056..3f4909d 100644
--- a/meta/rules-baseline.json
+++ b/meta/rules-baseline.json
@@ -13,6 +13,7 @@
"OOLD-CNF-1120": "a9cdf1bd1358785e00667c7d0ce7baf6dea8e8c1ad3736385ee92243ed38a2e6",
"OOLD-CNF-22d3": "b4e844d40d00838c2dbbc8d722aa9f35f85211917d61d5b453ea1ecafbf5a2a0",
"OOLD-CNF-d71d": "bbaaecdeec3c9419d4056db8917d92c8b14f3bd0af5c6d9c765c55814f3976b9",
+ "OOLD-EXT-05d3": "07e05da8b2fcd62c4560330cca1372fbfe129432a17fa660040f417566c43dc9",
"OOLD-EXT-1dc8": "8874d4e1da929ee9f25b4925a9820211469c1bc397b80760fdf25be82cf1d49b",
"OOLD-EXT-1e3c": "a10af104ed72237be86bd01f683041cf51fae97707f843e0906d22c53016011a",
"OOLD-EXT-1f92": "5d4c4a4f84533b109985d2562ad3459f3c9ce3dcd42d66aebeca8ca6c182e815",
@@ -29,8 +30,10 @@
"OOLD-EXT-61aa": "aa1601aa9ebbaf66046ee3c18acd7690ee8da151843196a42f3d4c1958ed42cd",
"OOLD-EXT-6312": "24f4abaced11e4203298124cfbc2642efc2b6b0fc4c9a3369e0ff4c8d16ce1b8",
"OOLD-EXT-68fa": "a71753a84fe8aa58779eb057aa8655918b06b17fea6e6e4ac0624bd780402012",
+ "OOLD-EXT-6d10": "f7aef6a0c109e10d28813bf45fe0808edbb5efdfab22edac7a171cd13a743f4d",
"OOLD-EXT-6ea3": "a1fd73169ee2f84b0a897e74e5495e9019dd4a8ac1c763a108fc8cb02a0afe9e",
"OOLD-EXT-7256": "1d8752aa41f9ee074a8b56e0e957c837218642db3fe32699d62cd0e5a669456b",
+ "OOLD-EXT-725f": "ceb79118ae8684c9a82b93e11eb96ec7ebd9d2515dbc087ca1c5afecd1f6fb57",
"OOLD-EXT-7c5d": "c427f66d6c7aff32fe62825b744d49ce754281ed070248a0d31324295985cf1e",
"OOLD-EXT-8f62": "6c1a14a9767b73741bc1b264af5d472191cb51e19cf4c4daa824ba35c814efc3",
"OOLD-EXT-adcc": "8396100be9d471ae2bf7a2767ad2b384b737e72888ccc960b9dcaf00616f13ac",
@@ -44,6 +47,7 @@
"OOLD-EXT-eeda": "d8d6a2f9158b5aa5a43dbc028f0e2bee5b963eb927309107ba16e4a505cc1667",
"OOLD-EXT-ef09": "f283baf414a7d41c21181aab27a32af73c618fa651fb20cb8d9ad80392063cf6",
"OOLD-EXT-fdd8": "c4bcd0af638b79ed0dc4ec4e25a597d38f2b3a957fab6ec77075e728b5887712",
+ "OOLD-EXT-ff64": "52ad7df4c37e1dc4253a1db04f928fc84099a704077d2b5c35d74807b72abf2c",
"OOLD-INS-1d33": "0ca31c621870dd1904ce0662b98d9c3633da1d2fa2eb149d4d7c592407a26ba3",
"OOLD-INS-1df7": "28cd0706353c61d3b420c02a6d00890ec47cf9b6458398ef1292d3389d0a3578",
"OOLD-INS-27aa": "5fe40a0c4116c197a46477bc16e26da36e667f9c1f2cdccc812a20078affaf30",
diff --git a/spec/sections/09-extensions.md b/spec/sections/09-extensions.md
index 59113fb..db7f9dd 100644
--- a/spec/sections/09-extensions.md
+++ b/spec/sections/09-extensions.md
@@ -95,6 +95,10 @@ Normalized - a single, consistent representation:
This makes an OO-LD schema bidirectional: its `@context` drives expansion (JSON to RDF), and the frame derived from its structure drives framing (RDF back to the schema's JSON tree). :rule[OOLD-EXT-68fa]{applies=implementation level=MUST summary="A frame derived from a schema takes its @type from the class type, embeds inlined object properties as subframes and keeps reference-valued properties as IRIs with @embed @never."}A tool that derives a frame MUST derive it mechanically: the schema's class type becomes the frame `@type`; an inlined object property becomes a nested subframe that embeds the referenced node; a reference-valued property (including one whose term is mapped with JSON-LD `@reverse`) becomes a subframe with `@embed: @never` so its targets stay IRIs, in line with the inline-versus-reference choice `x-oold-range` already records; and `@explicit` / `@requireAll` / `@default` follow from `additionalProperties` and `required`. The frame's `@context` is the composition of the referenced schemas' contexts.
+:rule[OOLD-EXT-6d10]{applies=implementation level=MUST summary="A frame derivation recognizes a property as reference-valued from x-oold-range, an IRI-family format, or a term mapped @type @id."}A derivation MUST recognize a property as **reference-valued** from any of three signals: an [`x-oold-range`](#range-of-properties) on a string-typed value, an IRI-family `format` (the family [](#range-reference-form) recommends), or a `@context` term mapped `"@type": "@id"`. :rule[OOLD-EXT-ff64]{applies=implementation level=MUST summary="Where a property matches both the embedded-object and the reference-valued signals, the embedded-object reading wins."}Where a property matches both this and the embedded-object shape, the embedded-object reading MUST win: a property whose value is an object is an embed whatever its term declares. :rule[OOLD-EXT-05d3]{applies=implementation level="MUST NOT" summary="A frame derivation must not emit a subframe for a property whose key aliases a JSON-LD keyword."}A derivation MUST NOT emit a subframe for a property whose key aliases a JSON-LD keyword - conventionally `id` for `@id` and `type` for `@type` (see [](#identity)). Such a key names the node rather than pointing at another one, and a subframe under it produces `{"@id": {...}}`, which a processor rejects.
+
+Omitting `@embed: @never` is not a cosmetic difference. A reference whose target happens to carry triples in the same graph is then embedded as an object, and the framed document no longer validates against the schema the frame was derived from, which declares a string in that position.
+
:::example{title="RDF to OO-LD schema to frame to instance"}
An input RDF graph (Turtle) - an organization with an address, and two persons who work for it:
```turtle
@@ -178,6 +182,16 @@ Framing the graph with that frame yields an OO-LD instance document, projected o
```
:::
+##### Many documents, one graph {#framing-many-documents}
+
+The example above frames a graph that happens to hold one matching node. In general a graph holds many, and the relationship runs in both directions.
+
+:rule[OOLD-EXT-725f]{applies=implementation level=MUST summary="Framing a graph that holds several nodes matching a derived frame yields one instance document per match."}Framing a graph with a schema-derived frame MUST yield **one instance document per matching node**, delivered as a `@graph` of matches rather than an arbitrary single root. Dually, several instance documents - each expanded through its own `$schema` - merge into one graph, because expansion assigns every node its own IRI and identical IRIs denote the same node.
+
+The `@embed: @never` derivation is what keeps the two directions consistent. A reference that stays a reference belongs to exactly one document: the one describing the node it is stored on. If framing embedded it instead, the target's triples would be copied into every document that references it, so a node's description would depend on which document a reader happened to open, and merging the documents back would re-assert those triples from several places at once. Keeping references as IRIs means each document round-trips independently and their union is the graph they came from.
+
+This is the framing-based answer to the object-graph mapping question: a schema does not describe a document in isolation, it describes how to cut one document out of a graph and how to put it back.
+
Framing is not the only option for this transformation. When the source data lives in a triplestore or behind a SPARQL endpoint, a [SPARQL](https://www.w3.org/TR/sparql11-query/) `CONSTRUCT` query - also derivable from the schema - can select and reshape the relevant subgraph directly, including deriving reverse relations such as `employees` from the inverse of `schema:worksFor`; compacting that result with the schema's `@context` (and framing it where nesting is required) yields the same OO-LD instance.
### JSON Schema {#jsonschema-extensions}