diff --git a/docs/standards/README.md b/docs/standards/README.md
index 3d05f9c..daef213 100644
--- a/docs/standards/README.md
+++ b/docs/standards/README.md
@@ -135,3 +135,104 @@ To update a reference:
The catalog schema version changes when its structure changes. The checker
keeps an explicit expected specification inventory; adding or removing a
standard requires a reviewed change to that inventory as well as the catalog.
+
+## Requirement-to-test evidence
+
+[coverage.md](coverage.md) is the readable feature and requirement map;
+[requirements.json](requirements.json) is its declarative source. A row names
+a concrete, scoped behavior, its implementation owner and actual collected
+tests. It does not establish every possible input or every clause of a copied
+standard. The map is not a percentage of specification conformance and is not
+a complete cross-product of formats, terms and execution environments.
+
+The categories distinguish:
+
+- `normative`: a concrete behavior supported by at least one verified normative
+ clause. For example, RDF literal identity depends on lexical forms, not merely
+ equal interpreted values. A reference to an informative explanation alone
+ cannot qualify a row as normative.
+- `policy`: an API boundary, rendering choice or ordering algorithm owned by
+ this project. Referenced standard clauses constrain or explain the behavior;
+ they do not make the chosen algorithm a standard algorithm.
+- `extension`: an explicitly bounded behavior outside the copied standards'
+ core data model, such as Python mixed-key input or dependency-supported
+ directional literals. This does not enlarge the conformance profile.
+
+The source and installed suites validate the map during ordinary pytest runs.
+Only the standards integration fixture requests a full collect-only subprocess,
+lazily and once per test session. Collection imports test modules but never
+executes test bodies or recursively validates the catalog. A focused command
+outside `tests/standards/` runs only its selected tests.
+
+```bash
+python scripts/check_standards.py
+python scripts/check_requirements.py
+python -m pytest -q --package-under-test=source tests/standards
+```
+
+The requirement command requires the project's pytest development dependency
+and installed RDF dependencies, but no extra plugin and no network access. Its
+child uses the same Python executable, isolated mode, a temporary working
+directory, absolute test/config paths and an explicit package target. Inherited
+pytest options and plugin requests are removed. The package inventory is read
+only after the test harness selects and verifies the package origin.
+
+For a non-editable wheel environment, use its Python with the checkout's
+script and assets:
+
+```bash
+/path/to/wheel-env/bin/python -I /path/to/diffable-rdf/scripts/check_requirements.py \
+ --package-under-test=installed
+```
+
+The report counts collected test matches, not executed or passing tests. The
+full test run remains the execution gate. The two source-only package-harness
+tests remain collected but skip under installed mode, and the address-space
+measurement skips on platforms without resource limits. These outcomes are
+reported by pytest; a collected selector is not interpreted as a passing result.
+
+### Requirement catalog schema
+
+Schema version 1 has exactly three top-level fields: `schema_version`,
+`features` and `requirements`. Unknown fields, enum values and duplicate JSON
+keys fail validation. Collections use the following fields:
+
+| Object | Fields and invariants |
+| --- | --- |
+| Callable feature | Unique `id`, `kind: callable`, `description`, and `export`. Exports must equal the selected package's actual public callable exports. |
+| Format feature | Unique `id`, `kind: format`, `description`, `aliases`, and `backend`. The complete alias-to-backend mapping must equal the selected package's `_FORMAT_MAP`, including synonym membership. |
+| Requirement | Unique stable uppercase-hyphenated `id`, `description`, known `features`, `category`, `clauses`, nonempty `owners`, `status`, `reason`, `evidence`, and `not_applicable`. Every feature needs supported evidence. |
+| Clause | `reference`, exact `section`, `anchor`, and boolean `normative`. HTML entries must match the verified reference manifest's anchor, heading label and normative status. Numbered RFC text sections use a null anchor and must match a complete heading in the checked original text. |
+| Owner | `role`, `target`, and `path`. Roles are `local` contract/algorithm, `dependency`-owned syntax/canonicalization or binding data, and `boundary` validation. Targets are actual local callable entry points in the selected package; paths must match their source files. A dependency role identifies the local call site, not an independent implementation of that backend. |
+| Supported row | `status: supported`, null `reason`, and nonempty `evidence`. Each dimension appears either in evidence or in `not_applicable`, never both. |
+| Excluded row | `status: out-of-profile` and a nonempty `reason`. Both evidence maps must be empty; exclusions are visible profile boundaries, not silently passing tests. |
+
+The six evidence dimensions are `positive`, `negative`, `boundary`, `property`,
+`subprocess` and `end-to-end`. Evidence values are nonempty lists of pytest
+selectors. `not_applicable` values explain why the dimension is not separately
+claimed in that row: a no-argument snapshot has no invalid-input domain, for
+example, and serializer term fidelity does not require syntax-parser rejection
+tests. Shared process or composed-workflow obligations can be mapped in their
+own rows. Explicit examples do not imply an independent randomized property.
+
+A selector is either an exact collected nodeid, such as
+`tests/contracts/test_prefix_map.py::test_prefix_map_calls_return_independent_snapshots`,
+or a function-family selector matching that exact name followed by pytest's
+`[` parameter suffix. Module paths alone, globs, substring matches, nonexistent
+functions and empty evidence sets are rejected. A function with both a bare
+nodeid and parametrized children is ambiguous and fails validation. Renaming
+or removing a referenced test therefore fails the gate even if its file remains.
+
+When changing the feature or evidence set:
+
+1. Read the selected tests' assertions and keep each row no broader than the
+ concrete evidence. Identify local versus dependency ownership explicitly.
+2. Add verified anchors only from the same pinned original bytes. Distinguish
+ normative grammar and algorithms from informative examples. Original assets
+ and their hashes need not change when the selected anchor inventory grows.
+3. Update feature, requirement and evidence entries together; do not make a
+ supported behavior out of profile merely because a test fails.
+4. Run `python scripts/check_requirements.py --render` to explicitly update the
+ generated table, then run the default check. The default never rewrites it.
+5. Run the full source and exact installed-wheel suites. The source distribution
+ carries documents, scripts and tests; runtime wheels carry none of these assets.
diff --git a/docs/standards/coverage.md b/docs/standards/coverage.md
new file mode 100644
index 0000000..4f033bb
--- /dev/null
+++ b/docs/standards/coverage.md
@@ -0,0 +1,1111 @@
+# Scoped requirement evidence
+
+Generated from [requirements.json](requirements.json) by `python scripts/check_requirements.py --render`.
+The default command checks this table without modifying it.
+
+49 supported requirements; 5 explicit profile exclusions; 813 distinct collected tests; 942 requirement-to-test links.
+
+Counts describe collected evidence, not executed or passing tests. One test can support several rows.
+This is not a percentage of all clauses in the copied specifications or a complete Cartesian test matrix.
+Source and installed targets collect the same behavioral evidence; platform/resource and source-only
+harness skips are reported by pytest when executing the suite, not hidden by collection counts.
+
+## Feature inventory
+
+| Feature | Contract | Export or format aliases |
+| --- | --- | --- |
+| `deterministic_turtle` | Diff-stable Turtle graph rendering | `deterministic_turtle` |
+| `canonicalize_rdf_graph` | Single-graph serialization with backend canonical labels | `canonicalize_rdf_graph` |
+| `deterministic_json` | Deterministic JSON ordering with bounded JSON-LD ordering awareness | `deterministic_json` |
+| `well_known_prefix_map` | Independent namespace-to-prefix mappings | `well_known_prefix_map` |
+| `wl_blank_node_labels` | Structural labels for canonicalized quads | `wl_blank_node_labels` |
+| `wl_relabel_quads` | Relabelled copies of canonicalized quads | `wl_relabel_quads` |
+| `format.turtle` | Turtle graph output | `turtle`, `ttl` |
+| `format.nt` | N-Triples graph output | `nt`, `ntriples`, `n-triples`, `nt11` |
+| `format.nquads` | N-Quads graph output | `nquads`, `n-quads` |
+| `format.xml` | RDF/XML graph output | `xml`, `rdf/xml` |
+| `format.trig` | TriG graph output | `trig` |
+| `format.n3` | N3 graph output | `n3` |
+| `format.jsonld` | JSON-LD graph output | `json-ld`, `jsonld`, `application/ld+json` |
+
+## Requirement map
+
+| Requirement | Category / status | Concrete behavior | Tests |
+| --- | --- | --- | --- |
+| [API-SURFACE](#api-surface) | policy / supported | Six public functions have the documented exports and resolvable runtime annotations. | 10 |
+| [API-EXAMPLES](#api-examples) | policy / supported | Eight executable documentation examples run against the selected package; six signature blocks parse as single function definitions. | 30 |
+| [GRAPH-ADMISSION](#graph-admission) | policy / supported | Graph serializers admit individual graph contexts and reject Dataset and ConjunctiveGraph containers before dispatch. | 59 |
+| [GRAPH-IMMUTABILITY](#graph-immutability) | policy / supported | Serialization retains caller triples, base, bindings and global literal settings in the checked native and degraded cases. | 68 |
+| [FORMAT-DETERMINISM](#format-determinism) | policy / supported | Every guaranteed alias has byte-identical output across hash-seeded processes on a shared-list graph. | 22 |
+| [FORMAT-ALIASES](#format-aliases) | policy / supported | Aliases select the same backend syntax; representative mixed-case names and degraded synonyms preserve their contract. | 19 |
+| [FORMAT-DELEGATION](#format-delegation) | policy / supported | Unmapped plugins are explicitly outside the determinism guarantee; unknown plugins and empty delegated output fail visibly. | 5 |
+| [FORMAT-FRAMING](#format-framing) | policy / supported | Nonempty mapped RDF output has exactly one trailing newline on normal and carrying degraded paths. | 41 |
+| [FORMAT-EMPTY](#format-empty) | policy / supported | Empty Turtle and N-Triples stay empty; empty JSON-LD is a newline-terminated empty array. | 1 |
+| [GRAPH-DEFAULT](#graph-default) | normative / supported | Single-graph N-Quads has no graph label; degraded TriG carries explicit default-graph list triples. | 27 |
+| [RDF-ISOMORPHISM](#rdf-isomorphism) | normative / supported | Seeded Turtle graph shapes preserve RDF graph identity after serialization and parsing. | 40 |
+| [TURTLE-STABILITY](#turtle-stability) | policy / supported | Seeded Turtle output is idempotent and independent of insertion order and source blank-node names. | 120 |
+| [RDF-LEXICAL](#rdf-lexical) | normative / supported | Checked integer, boolean, dateTime, decimal, signed-zero and non-finite literal spellings retain lexical term identity. | 18 |
+| [RDF-LANGUAGE](#rdf-language) | normative / supported | Plain strings and xsd:string render equivalently; normal-path language tags use the permitted lowercase spelling. | 4 |
+| [RDF-IRI-BASE](#rdf-iri-base) | normative / supported | Fragment, path, query and equal-base namespaces preserve direct and datatype IRIs in the checked verified renderings. | 43 |
+| [RDF-DATATYPE-GUARD](#rdf-datatype-guard) | policy / supported | Round-trip boundary checks distinguish missing or invented datatype IRIs from xsd:string literal equivalence. | 6 |
+| [TURTLE-QUOTING](#turtle-quoting) | normative / supported | Quoted literals preserve terminal quote/backslash runs, LF, CR and CRLF in checked native and relative-subject outputs. | 31 |
+| [TURTLE-NAMES](#turtle-names) | normative / supported | Punctuation, Unicode-prefix and overlapping-prefix examples preserve subject, predicate, object and datatype IRIs. | 67 |
+| [TURTLE-COLLECTIONS](#turtle-collections) | normative / supported | Private, shared-head and shared-tail list examples retain cell identity; the degraded path uses explicit list triples. | 9 |
+| [TURTLE-PRESENTATION](#turtle-presentation) | policy / supported | Diff-stable Turtle uses quoted typed literals and inline blank nodes where the graph shape permits. | 3 |
+| [NAMESPACES-ISOLATION](#namespaces-isolation) | policy / supported | Namespace selection preserves caller bindings, reserves generated names and remains stable across processes in checked graph shapes. | 18 |
+| [SERIALIZER-VERIFICATION](#serializer-verification) | policy / supported | Verified renderings remove base then prefixes only after failed fidelity checks; final validation and unrelated backend errors propagate. | 15 |
+| [LINE-TERMS](#line-terms) | normative / supported | Line syntaxes refuse relative or malformed IRIs and name the offending term position rather than emit invalid N-Triples or N-Quads. | 11 |
+| [LINE-SEPARATORS](#line-separators) | policy / supported | The internal line sorter preserves Unicode separators inside literals and sorts complete statement lines. | 18 |
+| [XML-LITERALS](#xml-literals) | normative / supported | Checked RDF/XML literals retain CR, CRLF, tabs, Unicode, markup escapes, datatypes and language identity after parsing. | 9 |
+| [XML-CHARACTERS](#xml-characters) | normative / supported | RDF/XML rejects XML 1.0-forbidden characters in literal content and serializer metadata. | 6 |
+| [XML-NAMESPACE](#xml-namespace) | policy / supported | Checked RDF/XML namespace declarations survive sorting; an inexpressible generalized literal predicate surfaces its backend QName error. | 3 |
+| [XML-ORDERING](#xml-ordering) | policy / supported | Degraded RDF/XML orders descriptions and property elements without altering graph meaning or caller prefixes. | 6 |
+| [JSONLD-GRAPH](#jsonld-graph) | normative / supported | The normal expanded JSON-LD backend preserves shared RDF list cells instead of compacting them into duplicate lists. | 2 |
+| [JSONLD-FALLBACK](#jsonld-fallback) | normative / supported | Expanded degraded JSON-LD retains shared cells, references, cycles and literal annotation strings. | 33 |
+| [JSONLD-TERM-BOUNDARY](#jsonld-term-boundary) | policy / supported | The degraded JSON-LD subset refuses generalized term positions, relative predicate/datatype IRIs and URIRef values resembling blank-node identifiers. | 10 |
+| [JSONLD-RESERVED](#jsonld-reserved) | policy / supported | The degraded writer refuses keyword-shaped relative identifiers, while keyword-shaped literal text and non-keyword identifier spellings survive. | 75 |
+| [JSON-ORDERING](#json-ordering) | policy / supported | Object keys and application-unordered arrays sort deterministically without mutation; this changes ordinary JSON array order by project policy. | 4 |
+| [JSON-MIXEDKEYS](#json-mixedkeys) | extension / supported | Mixed Python keys sort by their encoded names, and duplicate encoded names are refused before JSON object information can be lost. | 4 |
+| [JSON-LISTS](#json-lists) | normative / supported | Explicit and locally declared lists retain order, including nested lists, while graph/set arrays may be reordered without changing the checked RDF meaning. | 9 |
+| [JSON-CONTEXTS](#json-contexts) | policy / supported | Local ordering facts respect sequential context overrides, inheritance, null resets and checked forward alias definitions without claiming a full context processor. | 9 |
+| [JSON-LITERALS](#json-literals) | normative / supported | Explicit and locally recognized JSON literal payload arrays retain their sequence and checked RDF meaning. | 5 |
+| [JSON-UNKNOWN](#json-unknown) | policy / supported | Unknown remote, scoped and unsupported local contexts conservatively preserve descendant arrays and never fetch remote contexts. | 5 |
+| [JSON-OVERRIDES](#json-overrides) | policy / supported | Custom preserved-key sets replace convenience defaults without disabling keyword-based list protection. | 4 |
+| [WL-IDENTITY](#wl-identity) | policy / supported | WL relabelling returns a fresh isomorphic quad list, preserves blank-node cardinality and leaves input quads untouched. | 10 |
+| [WL-COMPONENTS](#wl-components) | policy / supported | Unrelated additions, edits and removals do not advance a disconnected component's default refinement. | 9 |
+| [WL-TIES](#wl-ties) | policy / supported | Structurally tied nodes remain injective and collision suffixes follow numeric canonical numbering. | 4 |
+| [WL-GRAPHS](#wl-graphs) | policy / supported | Named graphs influence labels; blank graph-name identifiers remain consistent when also used as object terms. | 4 |
+| [WL-ITERATIONS](#wl-iterations) | policy / supported | Default refinement reaches component fixpoints, explicit rounds remain synchronous, zero is valid and negative counts are refused. | 7 |
+| [WL-BOUNDARY](#wl-boundary) | policy / supported | Embedded triple objects are rejected consistently at zero, explicit and default refinement rounds. | 33 |
+| [WL-DIRECTION](#wl-direction) | extension / supported | Dependency-supported directional literals remain distinct and retain language and direction fields during WL relabelling. | 1 |
+| [WORK-TIME](#work-time) | policy / supported | A representative 8400-triple OWL-shaped graph serializes within the tested process time budget. | 1 |
+| [WORK-MEMORY](#work-memory) | policy / supported | A representative sixteen-round WL workload fits the tested address-space budget where the platform supports limits. | 1 |
+| [PREFIX-MAP](#prefix-map) | policy / supported | Prefix maps invert the selected RDFLib nonempty bindings into independent string dictionaries. | 3 |
+| [PROFILE-RDFC-PROCESSOR](#profile-rdfc-processor) | normative / out-of-profile | Standalone RDFC processor conformance, canonical N-Quads bytes and hash selection are outside the wrapper API. | 0 |
+| [PROFILE-RDFC-LIMITS](#profile-rdfc-limits) | normative / out-of-profile | Configurable RDFC resource-limit reporting is not exposed by the graph serializer API. | 0 |
+| [PROFILE-JSONLD-PROCESSOR](#profile-jsonld-processor) | normative / out-of-profile | Full JSON-LD expansion, compaction, remote loading and processing-algorithm conformance are outside the ordering helper. | 0 |
+| [PROFILE-N3-LOGIC](#profile-n3-logic) | policy / out-of-profile | Notation3 formulas, implications and variable-based logic are outside the supported N3 graph subset. | 0 |
+| [PROFILE-RDF12](#profile-rdf12) | extension / out-of-profile | Complete RDF 1.2 or embedded-triple conformance is outside the accepted WL term extension. | 0 |
+
+### API-SURFACE
+
+Six public functions have the documented exports and resolvable runtime annotations.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `deterministic_json`, `well_known_prefix_map`, `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.turtle:well_known_prefix_map`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_public_api_surface.py::test_the_documented_surface_is_what_the_module_exports` (1)
`tests/contracts/test_public_api_surface.py::test_every_public_annotation_resolves_at_runtime` (6) |
+| negative | Not applicable: Export enumeration and annotation inspection take no caller data to reject. |
+| boundary | `tests/contracts/test_public_api_surface.py::test_the_turtle_entry_point_names_the_graph_type_it_takes` (1)
`tests/contracts/test_public_api_surface.py::test_the_preserved_keys_argument_admits_any_set` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/contracts/test_public_api_surface.py::test_both_dependencies_are_loaded_before_any_public_call_can_be_made` (1) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### API-EXAMPLES
+
+Eight executable documentation examples run against the selected package; six signature blocks parse as single function definitions.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `deterministic_json`, `well_known_prefix_map`, `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.turtle:well_known_prefix_map`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/integration/test_documentation_examples.py::test_documentation_classifies_every_python_block` (1)
`tests/integration/test_documentation_examples.py::test_documented_signature_parses` (6) |
+| negative | `tests/integration/test_documentation_examples.py::test_broken_documentation_example_fails_in_the_selected_interpreter` (1) |
+| boundary | `tests/integration/test_documentation_examples.py::test_extractor_rejects_missing_and_invalid_markers` (14) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | `tests/integration/test_documentation_examples.py::test_documented_example_runs_from_a_neutral_directory` (8) |
+
+### GRAPH-ADMISSION
+
+Graph serializers admit individual graph contexts and reject Dataset and ConjunctiveGraph containers before dispatch.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`.
+
+References: [RDF11-CONCEPTS §3.1 Triples](references/rdf11-concepts.html#section-triples) (normative).
+
+- boundary: [`diffable_rdf.graph_input:_require_single_graph`](../../src/diffable_rdf/graph_input.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_graph_input.py::test_individual_dataset_graph_context_is_accepted_losslessly` (8)
`tests/contracts/test_graph_input.py::test_ordinary_graph_with_context_aware_store_is_accepted` (2) |
+| negative | `tests/contracts/test_graph_input.py::test_dataset_containers_are_rejected_without_mutation` (32)
`tests/contracts/test_graph_input.py::test_canonicalize_rejects_datasets_before_every_format_dispatch` (16) |
+| boundary | `tests/contracts/test_graph_input.py::test_canonicalize_rejects_dataset_before_inspecting_format` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### GRAPH-IMMUTABILITY
+
+Serialization retains caller triples, base, bindings and global literal settings in the checked native and degraded cases.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_literal_fidelity.py::test_serialization_does_not_mutate_input_or_global_normalization` (1)
`tests/serialization/test_prefixed_names.py::test_prefixed_names_preserve_every_term_position` (60) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_xml_fidelity.py::test_rdf_xml_is_stable_and_does_not_mutate_input_or_global_settings` (1)
`tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_aliases_are_deterministic_and_leave_input_unchanged` (6) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### FORMAT-DETERMINISM
+
+Every guaranteed alias has byte-identical output across hash-seeded processes on a shared-list graph.
+
+Features: `canonicalize_rdf_graph`, `format.turtle`, `format.nt`, `format.nquads`, `format.xml`, `format.trig`, `format.n3`, `format.jsonld`.
+
+References: [RDFC10 §3.1 Terms defined by this specification](references/rdf-canon.html#canon-terms) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_format_guarantee.py::test_canonicalize_rdf_graph_is_deterministic_across_processes` (7) |
+| negative | Not applicable: Format admission and delegated-name boundaries are mapped separately in FORMAT-DELEGATION. |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/contracts/test_format_guarantee.py::test_every_mapped_format_is_byte_identical_across_processes` (15) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### FORMAT-ALIASES
+
+Aliases select the same backend syntax; representative mixed-case names and degraded synonyms preserve their contract.
+
+Features: `canonicalize_rdf_graph`, `format.turtle`, `format.nt`, `format.nquads`, `format.xml`, `format.trig`, `format.n3`, `format.jsonld`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_format_guarantee.py::test_mapped_formats_cover_every_name_the_docstring_promises` (1)
`tests/contracts/test_format_guarantee.py::test_canonicalize_rdf_graph_accepts_mixed_case_format_names` (3)
`tests/serialization/test_degraded_format_coverage.py::test_synonyms_for_one_format_produce_identical_bytes` (3) |
+| negative | `tests/serialization/test_degraded_format_coverage.py::test_synonyms_refuse_for_the_same_reason` (2) |
+| boundary | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_aliases_are_deterministic_and_leave_input_unchanged` (6)
`tests/serialization/test_xml_fidelity.py::test_rdf_xml_aliases_use_exact_round_trip_guard` (4) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### FORMAT-DELEGATION
+
+Unmapped plugins are explicitly outside the determinism guarantee; unknown plugins and empty delegated output fail visibly.
+
+Features: `canonicalize_rdf_graph`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- boundary: [`diffable_rdf.canonicalize:_deterministic_fallback_serialize`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_format_guarantee.py::test_delegating_to_an_rdflib_plugin_warns_that_the_guarantee_does_not_apply` (1)
`tests/contracts/test_format_guarantee.py::test_a_delegated_format_still_gets_canonical_blank_node_labels` (1) |
+| negative | `tests/contracts/test_format_guarantee.py::test_an_unregistered_format_name_still_surfaces_rdflibs_own_error` (1)
`tests/serialization/test_fallback_integrity.py::test_a_delegated_format_that_writes_nothing_is_refused_not_returned` (1) |
+| boundary | `tests/serialization/test_degraded_format_coverage.py::test_an_unregistered_name_still_reaches_rdflib_on_the_degraded_path` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### FORMAT-FRAMING
+
+Nonempty mapped RDF output has exactly one trailing newline on normal and carrying degraded paths.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.nt`, `format.nquads`, `format.xml`, `format.trig`, `format.n3`, `format.jsonld`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.canonicalize:_with_single_trailing_newline`](../../src/diffable_rdf/canonicalize.py).
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_trailing_newline.py::test_every_mapped_format_ends_with_exactly_one_newline` (15)
`tests/contracts/test_trailing_newline.py::test_deterministic_turtle_ends_with_exactly_one_newline` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/contracts/test_trailing_newline.py::test_the_degraded_path_ends_with_exactly_one_newline` (9)
`tests/contracts/test_trailing_newline.py::test_deterministic_turtle_ends_with_exactly_one_newline_when_degraded` (1)
`tests/contracts/test_trailing_newline.py::test_normalising_the_newline_does_not_add_a_blank_line` (15) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### FORMAT-EMPTY
+
+Empty Turtle and N-Triples stay empty; empty JSON-LD is a newline-terminated empty array.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.nt`, `format.jsonld`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_trailing_newline.py::test_an_empty_graph_stays_an_empty_document` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/contracts/test_trailing_newline.py::test_an_empty_graph_stays_an_empty_document` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### GRAPH-DEFAULT
+
+Single-graph N-Quads has no graph label; degraded TriG carries explicit default-graph list triples.
+
+Features: `canonicalize_rdf_graph`, `format.nquads`, `format.trig`.
+
+References: [NQUADS11 §2.1 Simple Statements](references/n-quads.html#simple-triples) (normative); [TRIG11 §5.3.1 Output Graph](references/trig.html#output-graph) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_format_coverage.py::test_line_oriented_output_still_works_when_the_graph_is_representable` (1)
`tests/serialization/test_degraded_format_coverage.py::test_degraded_trig_states_list_structure_explicitly` (1) |
+| negative | `tests/contracts/test_graph_input.py::test_canonicalize_rejects_datasets_before_every_format_dispatch` (16) |
+| boundary | `tests/serialization/test_degraded_format_coverage.py::test_degraded_output_preserves_the_graph` (9) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### RDF-ISOMORPHISM
+
+Seeded Turtle graph shapes preserve RDF graph identity after serialization and parsing.
+
+Features: `deterministic_turtle`.
+
+References: [RDF11-CONCEPTS §3.6 Graph Comparison](references/rdf11-concepts.html#section-graph-equality) (normative).
+
+- dependency: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/properties/test_canonicalization_properties.py::test_p1_canonical_output_is_lossless` (40) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | `tests/properties/test_canonicalization_properties.py::test_p1_canonical_output_is_lossless` (40) |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### TURTLE-STABILITY
+
+Seeded Turtle output is idempotent and independent of insertion order and source blank-node names.
+
+Features: `deterministic_turtle`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- dependency: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/properties/test_canonicalization_properties.py::test_p2_canonicalization_is_idempotent` (40) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | `tests/properties/test_canonicalization_properties.py::test_p2_canonicalization_is_idempotent` (40)
`tests/properties/test_canonicalization_properties.py::test_p3_output_does_not_depend_on_blank_node_labels` (40)
`tests/properties/test_canonicalization_properties.py::test_p4_output_does_not_depend_on_insertion_order` (40) |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### RDF-LEXICAL
+
+Checked integer, boolean, dateTime, decimal, signed-zero and non-finite literal spellings retain lexical term identity.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.trig`, `format.n3`.
+
+References: [RDF11-CONCEPTS §3.3 Literals](references/rdf11-concepts.html#section-Graph-Literal) (normative).
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_distinct_lexical_forms` (4)
`tests/serialization/test_literal_fidelity.py::test_lower_level_turtle_family_preserves_literal_identity` (3) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_full_double_precision` (1)
`tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_other_typed_lexical_forms` (2)
`tests/serialization/test_numeric_literal_edges.py::test_the_lexical_form_the_graph_holds_is_what_is_written` (8) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### RDF-LANGUAGE
+
+Plain strings and xsd:string render equivalently; normal-path language tags use the permitted lowercase spelling.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`.
+
+References: [RDF11-CONCEPTS §3.3 Literals](references/rdf11-concepts.html#section-Graph-Literal) (normative).
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_literal_fidelity.py::test_intentional_string_and_language_equivalences_remain_canonical` (1)
`tests/integration/test_diffable_rdf.py::test_canonicalize_rdf_graph_normalizes_language_tag_case` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_base_iri_fidelity.py::test_the_guard_accepts_plain_string_and_xsd_string_datatype_equivalence` (1)
`tests/serialization/test_base_iri_fidelity.py::test_the_guard_keeps_a_direct_xsd_string_iri_significant` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### RDF-IRI-BASE
+
+Fragment, path, query and equal-base namespaces preserve direct and datatype IRIs in the checked verified renderings.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.trig`, `format.n3`, `format.xml`.
+
+References: [TURTLE11 §6.3 IRI References](references/turtle.html#sec-iri-references) (normative); [RFC3986 §5.2 Relative Resolution](references/rfc3986.txt) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+- dependency: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_base_iri_fidelity.py::test_a_fragment_base_preserves_the_terms` (6)
`tests/serialization/test_base_iri_fidelity.py::test_an_equal_base_namespace_preserves_every_iri_position` (20) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_base_iri_fidelity.py::test_deterministic_turtle_preserves_hash_path_and_slash_bases` (1)
`tests/serialization/test_base_iri_fidelity.py::test_an_unprefixed_query_base_retries_without_base_and_preserves_datatypes` (4)
`tests/serialization/test_base_iri_fidelity.py::test_a_datatype_only_query_base_retries_without_base` (4)
`tests/serialization/test_base_iri_fidelity.py::test_rdf_xml_retries_without_base_for_typed_literal_namespaces` (8) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### RDF-DATATYPE-GUARD
+
+Round-trip boundary checks distinguish missing or invented datatype IRIs from xsd:string literal equivalence.
+
+Features: `canonicalize_rdf_graph`.
+
+References: [RDF11-CONCEPTS §3.3 Literals](references/rdf11-concepts.html#section-Graph-Literal) (normative).
+
+- boundary: [`diffable_rdf.canonicalize:_assert_round_trips`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_base_iri_fidelity.py::test_the_guard_accepts_plain_string_and_xsd_string_datatype_equivalence` (1) |
+| negative | `tests/serialization/test_base_iri_fidelity.py::test_the_guard_catches_missing_and_invented_literal_datatypes` (2)
`tests/serialization/test_base_iri_fidelity.py::test_the_guard_catches_an_iri_the_input_never_contained` (1)
`tests/serialization/test_base_iri_fidelity.py::test_the_guard_keeps_a_direct_xsd_string_iri_significant` (1) |
+| boundary | `tests/serialization/test_base_iri_fidelity.py::test_the_guard_tolerates_rdflibs_literal_normalization` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### TURTLE-QUOTING
+
+Quoted literals preserve terminal quote/backslash runs, LF, CR and CRLF in checked native and relative-subject outputs.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.trig`, `format.n3`.
+
+References: [TURTLE11 §6.4 Escape Sequences](references/turtle.html#sec-escapes) (normative); [TURTLE11 §6.5 Grammar](references/turtle.html#sec-grammar-grammar) (normative); [RDF11-CONCEPTS §3.3 Literals](references/rdf11-concepts.html#section-Graph-Literal) (normative).
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+- local: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_numeric_literal_edges.py::test_literals_needing_escapes_still_round_trip` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_literal_quote_boundaries` (3)
`tests/serialization/test_literal_fidelity.py::test_turtle_aliases_preserve_literal_quote_boundaries` (12)
`tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_fallback_preserves_literal_quote_boundaries` (3)
`tests/serialization/test_literal_fidelity.py::test_turtle_alias_fallbacks_preserve_literal_quote_boundaries` (12) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### TURTLE-NAMES
+
+Punctuation, Unicode-prefix and overlapping-prefix examples preserve subject, predicate, object and datatype IRIs.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.trig`, `format.n3`.
+
+References: [TURTLE11 §6.5 Grammar](references/turtle.html#sec-grammar-grammar) (normative); [RFC3987 §2.2 ABNF for IRI References and IRIs](references/rfc3987.txt) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+- dependency: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_prefixed_names.py::test_prefixed_names_preserve_every_term_position` (60)
`tests/serialization/test_prefixed_names.py::test_overlapping_prefixes_and_aliases_remain_optional` (3) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_prefixed_names.py::test_curie_looking_literal_content_is_data` (3)
`tests/serialization/test_prefixed_names.py::test_deterministic_turtle_preserves_curie_looking_literal_content` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### TURTLE-COLLECTIONS
+
+Private, shared-head and shared-tail list examples retain cell identity; the degraded path uses explicit list triples.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`, `format.turtle`, `format.trig`, `format.n3`.
+
+References: [RDF11-CONCEPTS §3.6 Graph Comparison](references/rdf11-concepts.html#section-graph-equality) (normative); [TURTLE11 §2.8 Collections](references/turtle.html#collections) (informative context).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+- dependency: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/properties/test_canonicalization_properties.py::test_shared_list_structures_survive_canonicalization` (6) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_degraded_collections.py::test_degraded_turtle_is_lossless_for_shared_list_tails` (1)
`tests/serialization/test_degraded_collections.py::test_degraded_n3_is_lossless_for_shared_list_tails` (1)
`tests/serialization/test_degraded_format_coverage.py::test_degraded_trig_states_list_structure_explicitly` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### TURTLE-PRESENTATION
+
+Diff-stable Turtle uses quoted typed literals and inline blank nodes where the graph shape permits.
+
+Features: `deterministic_turtle`.
+
+References: [TURTLE11 §6.5 Grammar](references/turtle.html#sec-grammar-grammar) (normative).
+
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_literal_fidelity.py::test_typed_literals_are_written_in_quoted_form` (1)
`tests/integration/test_diffable_rdf.py::test_deterministic_turtle_uses_inline_blank_nodes` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_numeric_literal_edges.py::test_the_object_order_is_total_for_value_tied_and_mixed_terms` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### NAMESPACES-ISOLATION
+
+Namespace selection preserves caller bindings, reserves generated names and remains stable across processes in checked graph shapes.
+
+Features: `deterministic_turtle`, `canonicalize_rdf_graph`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.namespaces:prepare_namespaces`](../../src/diffable_rdf/namespaces.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_namespace_determinism.py::test_caller_prefixes_are_preserved_and_generated_names_are_reserved` (1) |
+| negative | `tests/serialization/test_namespace_determinism.py::test_an_undeclarable_binding_preserves_usable_prefixes` (1) |
+| boundary | `tests/serialization/test_namespace_determinism.py::test_deterministic_turtle_degraded_path_keeps_graph_and_bindings` (1)
`tests/serialization/test_namespace_determinism.py::test_the_prefix_filter_matches_what_pyoxigraph_will_accept` (10) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/serialization/test_namespace_determinism.py::test_namespace_allocation_is_stable_across_processes` (5) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### SERIALIZER-VERIFICATION
+
+Verified renderings remove base then prefixes only after failed fidelity checks; final validation and unrelated backend errors propagate.
+
+Features: `canonicalize_rdf_graph`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- boundary: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_base_iri_fidelity.py::test_a_verified_base_does_not_serialize_twice` (3)
`tests/serialization/test_prefixed_names.py::test_a_valid_compact_spelling_keeps_a_problematic_binding` (3) |
+| negative | `tests/serialization/test_prefixed_names.py::test_final_prefix_attempt_propagates_validation_failure` (1)
`tests/serialization/test_base_iri_fidelity.py::test_an_unrelated_backend_error_is_not_retried` (3)
`tests/serialization/test_xml_fidelity.py::test_rdf_xml_guard_rejects_corrupted_serializer_output` (2) |
+| boundary | `tests/serialization/test_prefixed_names.py::test_verification_failures_remove_options_in_order` (2)
`tests/serialization/test_prefixed_names.py::test_retry_options_do_not_affect_later_renderings` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### LINE-TERMS
+
+Line syntaxes refuse relative or malformed IRIs and name the offending term position rather than emit invalid N-Triples or N-Quads.
+
+Features: `canonicalize_rdf_graph`, `format.nt`, `format.nquads`.
+
+References: [NTRIPLES11 §2.2 IRIs](references/n-triples.html#sec-iri) (normative); [NTRIPLES11 §7 Grammar](references/n-triples.html#n-triples-grammar) (normative); [NQUADS11 §4 Grammar](references/n-quads.html#sec-grammar) (normative); [RDF11-CONCEPTS §3.1 Triples](references/rdf11-concepts.html#section-triples) (normative).
+
+- boundary: [`diffable_rdf.canonicalize:_first_term_n_triples_cannot_write`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_format_coverage.py::test_line_oriented_output_still_works_when_the_graph_is_representable` (1) |
+| negative | `tests/serialization/test_degraded_format_coverage.py::test_line_oriented_aliases_refuse_a_graph_they_cannot_represent` (6)
`tests/serialization/test_degraded_format_coverage.py::test_the_refusal_covers_an_iri_that_is_absolute_but_not_valid` (3) |
+| boundary | `tests/serialization/test_degraded_format_coverage.py::test_the_refusal_names_the_position_of_the_offending_term` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### LINE-SEPARATORS
+
+The internal line sorter preserves Unicode separators inside literals and sorts complete statement lines.
+
+Features: `canonicalize_rdf_graph`, `format.nt`, `format.nquads`.
+
+References: [NTRIPLES11 §7 Grammar](references/n-triples.html#n-triples-grammar) (normative); [NQUADS11 §4 Grammar](references/n-quads.html#sec-grammar) (normative).
+
+- local: [`diffable_rdf.canonicalize:_deterministic_fallback_serialize`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_fallback_integrity.py::test_the_line_sort_still_sorts` (2) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_fallback_integrity.py::test_the_line_sort_preserves_a_literal_carrying_a_unicode_break` (16) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This internal branch is tested directly; unrepresentable public line-format inputs are refused before it. |
+
+### XML-LITERALS
+
+Checked RDF/XML literals retain CR, CRLF, tabs, Unicode, markup escapes, datatypes and language identity after parsing.
+
+Features: `canonicalize_rdf_graph`, `format.xml`.
+
+References: [XML10 §2.11 End-of-Line Handling](references/xml.html#sec-line-ends) (normative); [RDF11-CONCEPTS §3.3 Literals](references/rdf11-concepts.html#section-Graph-Literal) (normative); [RDFXML11 §7 RDF/XML Grammar](references/rdf-syntax-grammar.html#section-Infoset-Grammar) (normative).
+
+- boundary: [`diffable_rdf.canonicalize:_finalize_rdf_xml`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_xml_fidelity.py::test_rdf_xml_preserves_diverse_literal_content_and_identity` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_xml_fidelity.py::test_rdf_xml_preserves_known_lossy_literal_values` (3)
`tests/serialization/test_xml_fidelity.py::test_rdf_xml_aliases_work_on_degraded_fallback` (4)
`tests/serialization/test_degraded_rdfxml_determinism.py::test_a_literal_carriage_return_still_survives_the_sort` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### XML-CHARACTERS
+
+RDF/XML rejects XML 1.0-forbidden characters in literal content and serializer metadata.
+
+Features: `canonicalize_rdf_graph`, `format.xml`.
+
+References: [XML10 §2.2 Characters](references/xml.html#charsets) (normative).
+
+- boundary: [`diffable_rdf.canonicalize:_assert_xml_10_representable`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_xml_fidelity.py::test_rdf_xml_preserves_diverse_literal_content_and_identity` (1) |
+| negative | `tests/serialization/test_xml_fidelity.py::test_rdf_xml_rejects_characters_forbidden_by_xml_10` (4) |
+| boundary | `tests/serialization/test_xml_fidelity.py::test_rdf_xml_rejects_forbidden_characters_in_serializer_metadata` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### XML-NAMESPACE
+
+Checked RDF/XML namespace declarations survive sorting; an inexpressible generalized literal predicate surfaces its backend QName error.
+
+Features: `canonicalize_rdf_graph`, `format.xml`.
+
+References: [XMLNS10 §4 Qualified Names](references/xml-names.html#ns-qualnames) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_rdfxml_determinism.py::test_the_callers_prefix_names_survive_the_sort` (1)
`tests/serialization/test_degraded_rdfxml_determinism.py::test_the_document_stays_well_formed_and_declared` (1) |
+| negative | `tests/serialization/test_fallback_integrity.py::test_a_fallback_serializer_error_names_the_format_and_a_way_forward` (1) |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### XML-ORDERING
+
+Degraded RDF/XML orders descriptions and property elements without altering graph meaning or caller prefixes.
+
+Features: `canonicalize_rdf_graph`, `format.xml`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.canonicalize:_sort_rdf_xml_descriptions`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_rdfxml_determinism.py::test_sorting_preserves_the_graph` (1)
`tests/serialization/test_degraded_rdfxml_determinism.py::test_property_elements_within_a_subject_are_ordered_too` (1)
`tests/serialization/test_degraded_rdfxml_determinism.py::test_properties_are_ordered_by_predicate_not_by_their_objects_label` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_degraded_rdfxml_determinism.py::test_the_output_does_not_depend_on_process_global_xml_state` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/serialization/test_degraded_rdfxml_determinism.py::test_degraded_rdf_xml_is_byte_identical_across_processes` (2) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSONLD-GRAPH
+
+The normal expanded JSON-LD backend preserves shared RDF list cells instead of compacting them into duplicate lists.
+
+Features: `canonicalize_rdf_graph`, `format.jsonld`.
+
+References: [JSONLD11 §9.2 Node Objects](references/json-ld11.html#node-objects) (normative); [RDF11-CONCEPTS §3.6 Graph Comparison](references/rdf11-concepts.html#section-graph-equality) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_collections.py::test_json_ld_is_lossless_for_shared_list_tails` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_degraded_jsonld.py::test_normal_json_ld_path_still_uses_pyoxigraph` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSONLD-FALLBACK
+
+Expanded degraded JSON-LD retains shared cells, references, cycles and literal annotation strings.
+
+Features: `canonicalize_rdf_graph`, `format.jsonld`.
+
+References: [JSONLD11 §9.2 Node Objects](references/json-ld11.html#node-objects) (normative); [JSONLD11 §9.5 Value Objects](references/json-ld11.html#value-objects) (normative); [RDF11-CONCEPTS §3.6 Graph Comparison](references/rdf11-concepts.html#section-graph-equality) (normative).
+
+- local: [`diffable_rdf.expanded_jsonld:serialize_expanded_jsonld`](../../src/diffable_rdf/expanded_jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_shared_cells_references_and_cycles` (1)
`tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_literal_lexical_strings_and_annotations` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_nonkeyword_identifiers_with_an_explicit_base` (30) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_is_reproducible_across_processes` (1) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSONLD-TERM-BOUNDARY
+
+The degraded JSON-LD subset refuses generalized term positions, relative predicate/datatype IRIs and URIRef values resembling blank-node identifiers.
+
+Features: `canonicalize_rdf_graph`, `format.jsonld`.
+
+References: [RDF11-CONCEPTS §3.1 Triples](references/rdf11-concepts.html#section-triples) (normative); [JSONLD11 §9.2 Node Objects](references/json-ld11.html#node-objects) (normative); [JSONLD11 §9.5 Value Objects](references/json-ld11.html#value-objects) (normative).
+
+- boundary: [`diffable_rdf.expanded_jsonld:serialize_expanded_jsonld`](../../src/diffable_rdf/expanded_jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_literal_lexical_strings_and_annotations` (1) |
+| negative | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_rejects_terms_outside_interoperable_subset` (8)
`tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_rejects_literal_predicates` (1) |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSONLD-RESERVED
+
+The degraded writer refuses keyword-shaped relative identifiers, while keyword-shaped literal text and non-keyword identifier spellings survive.
+
+Features: `canonicalize_rdf_graph`, `format.jsonld`.
+
+References: [JSONLD11-API §5.2 IRI Expansion](references/json-ld11-api.html#iri-expansion) (normative).
+
+- boundary: [`diffable_rdf.expanded_jsonld:_iri_identifier`](../../src/diffable_rdf/expanded_jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_keyword_shaped_literal_text` (15) |
+| negative | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_rejects_reserved_relative_identifiers` (30) |
+| boundary | `tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_nonkeyword_identifiers_with_an_explicit_base` (30) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-ORDERING
+
+Object keys and application-unordered arrays sort deterministically without mutation; this changes ordinary JSON array order by project policy.
+
+Features: `deterministic_json`.
+
+References: [RFC8259 §4 Objects](references/rfc8259.txt) (normative); [RFC8259 §5 Arrays](references/rfc8259.txt) (normative).
+
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/integration/test_diffable_rdf.py::test_deterministic_json_sorts_keys_and_lists` (1)
`tests/json/test_jsonld_order.py::test_context_free_json_still_sorts_lists_and_does_not_mutate_input` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | `tests/json/test_json_mixed_keys.py::test_parsed_json_rendering_is_idempotent` (1)
`tests/integration/test_diffable_rdf.py::test_deterministic_json_is_stable_regardless_of_insertion_order` (1) |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-MIXEDKEYS
+
+Mixed Python keys sort by their encoded names, and duplicate encoded names are refused before JSON object information can be lost.
+
+Features: `deterministic_json`.
+
+References: [RFC8259 §4 Objects](references/rfc8259.txt) (normative).
+
+- local: [`diffable_rdf.jsonld:_json_key`](../../src/diffable_rdf/jsonld.py).
+- boundary: [`diffable_rdf.jsonld:_sorted_items`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/json/test_json_mixed_keys.py::test_mixed_key_dictionary_list_permutations_are_deterministic` (1) |
+| negative | `tests/json/test_json_mixed_keys.py::test_encoded_key_collisions_are_refused_not_emitted_twice` (1)
`tests/json/test_json_mixed_keys.py::test_two_distinct_nan_keys_are_refused_as_well` (1) |
+| boundary | `tests/json/test_json_mixed_keys.py::test_non_string_keys_sort_by_the_name_json_writes_not_by_str` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-LISTS
+
+Explicit and locally declared lists retain order, including nested lists, while graph/set arrays may be reordered without changing the checked RDF meaning.
+
+Features: `deterministic_json`.
+
+References: [JSONLD11 §9.7 Lists and Sets](references/json-ld11.html#lists-and-sets) (normative); [JSONLD11 §4.3 Value Ordering](references/json-ld11.html#sets-and-lists) (informative context).
+
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/json/test_jsonld_order.py::test_context_defined_list_preserves_rdf_meaning` (1)
`tests/json/test_jsonld_order.py::test_explicit_list_preserves_rdf_meaning` (1)
`tests/json/test_jsonld_unordered_keys.py::test_sorting_an_unordered_container_does_not_change_the_rdf` (2) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/json/test_jsonld_order.py::test_an_array_nested_in_an_ordered_array_keeps_its_order` (2)
`tests/json/test_jsonld_unordered_keys.py::test_a_container_list_term_nested_in_a_set_array_keeps_its_order` (1)
`tests/json/test_jsonld_unordered_keys.py::test_an_aliased_list_nested_in_a_graph_array_keeps_its_order` (1)
`tests/json/test_jsonld_order.py::test_a_dict_inside_an_ordered_array_still_starts_a_sortable_node_object` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-CONTEXTS
+
+Local ordering facts respect sequential context overrides, inheritance, null resets and checked forward alias definitions without claiming a full context processor.
+
+Features: `deterministic_json`.
+
+References: [JSONLD11-API §4.1 Context Processing Algorithm](references/json-ld11-api.html#context-processing-algorithm) (normative); [JSONLD11-API §4.2 Create Term Definition](references/json-ld11-api.html#create-term-definition) (normative).
+
+- local: [`diffable_rdf.jsonld:_apply_local_context`](../../src/diffable_rdf/jsonld.py).
+- local: [`diffable_rdf.jsonld:_apply_term_definitions`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/json/test_jsonld_order.py::test_context_overrides_are_applied_in_order` (1)
`tests/json/test_jsonld_order.py::test_local_context_is_inherited_and_null_resets_it` (1)
`tests/json/test_jsonld_order.py::test_list_keyword_alias_preserves_rdf_meaning` (2) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/json/test_jsonld_order.py::test_a_term_definition_may_reference_an_alias_declared_after_it` (3)
`tests/json/test_jsonld_order.py::test_a_shadowing_definition_in_a_nested_context_array_keeps_its_order` (1)
`tests/json/test_jsonld_unordered_keys.py::test_a_context_inside_a_graph_element_is_still_applied` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-LITERALS
+
+Explicit and locally recognized JSON literal payload arrays retain their sequence and checked RDF meaning.
+
+Features: `deterministic_json`.
+
+References: [JSONLD11 §9.5 Value Objects](references/json-ld11.html#value-objects) (normative); [RFC8259 §5 Arrays](references/rfc8259.txt) (normative).
+
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/json/test_jsonld_order.py::test_json_literal_arrays_preserve_rdf_meaning` (4) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/json/test_jsonld_order.py::test_a_term_definition_may_reference_an_alias_declared_after_it[type-json-through-an-alias-declared-later]` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-UNKNOWN
+
+Unknown remote, scoped and unsupported local contexts conservatively preserve descendant arrays and never fetch remote contexts.
+
+Features: `deterministic_json`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.jsonld:_apply_local_context`](../../src/diffable_rdf/jsonld.py).
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/json/test_jsonld_order.py::test_scoped_context_fails_safe_for_the_document` (1)
`tests/json/test_jsonld_order.py::test_type_scoped_context_fails_safe_and_preserves_rdf_meaning` (1) |
+| negative | `tests/json/test_jsonld_order.py::test_remote_context_is_never_loaded_and_fails_safe` (1) |
+| boundary | `tests/json/test_jsonld_order.py::test_unsupported_context_directive_fails_safe` (2) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### JSON-OVERRIDES
+
+Custom preserved-key sets replace convenience defaults without disabling keyword-based list protection.
+
+Features: `deterministic_json`.
+
+References: [JSONLD11 §9.7 Lists and Sets](references/json-ld11.html#lists-and-sets) (normative).
+
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/json/test_jsonld_unordered_keys.py::test_a_custom_key_set_replaces_the_default_but_not_the_keyword_rules` (1)
`tests/contracts/test_public_api_surface.py::test_the_preserved_keys_argument_admits_any_set` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/json/test_jsonld_unordered_keys.py::test_imports_stays_protected_by_default_and_is_droppable` (1)
`tests/json/test_json_mixed_keys.py::test_custom_preserved_list_keeps_mixed_key_element_order` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-IDENTITY
+
+WL relabelling returns a fresh isomorphic quad list, preserves blank-node cardinality and leaves input quads untouched.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+References: [RDF11-CONCEPTS §3.6 Graph Comparison](references/rdf11-concepts.html#section-graph-equality) (normative).
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_contracts.py::test_wl_relabel_quads_preserves_the_graph` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/wl/test_wl_contracts.py::test_wl_functions_leave_supported_input_quads_unchanged` (9) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-COMPONENTS
+
+Unrelated additions, edits and removals do not advance a disconnected component's default refinement.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`, `deterministic_turtle`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_components.py::test_default_convergence_isolated_from_disconnected_changes` (3)
`tests/wl/test_wl_components.py::test_public_turtle_preserves_a_visible_disconnected_label` (1)
`tests/wl/test_wl_contracts.py::test_wl_relabel_quads_is_diff_stable` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/wl/test_wl_components.py::test_shared_blank_subject_object_roles_form_one_component` (1)
`tests/wl/test_wl_components.py::test_graph_membership_does_not_join_signature_independent_components` (2) |
+| property | `tests/wl/test_wl_components.py::test_component_labels_ignore_input_order_and_blank_node_names` (1) |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-TIES
+
+Structurally tied nodes remain injective and collision suffixes follow numeric canonical numbering.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_contracts.py::test_wl_blank_node_labels_are_injective` (1)
`tests/wl/test_wl_components.py::test_tied_components_remain_injective` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/wl/test_wl_components.py::test_the_tie_break_follows_the_canonical_numbering` (1)
`tests/wl/test_wl_components.py::test_adding_a_blank_node_does_not_relabel_the_tied_ones_before_it` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-GRAPHS
+
+Named graphs influence labels; blank graph-name identifiers remain consistent when also used as object terms.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_contracts.py::test_wl_distinguishes_identical_structure_in_different_named_graphs` (1)
`tests/wl/test_wl_contracts.py::test_wl_relabel_quads_remaps_blank_node_graph_names` (1) |
+| negative | Not applicable: This is a transformation of admitted values, not a syntax parser or a new input-rejection API. |
+| boundary | `tests/wl/test_wl_contracts.py::test_wl_labels_graph_name_only_blank_nodes_by_content` (1)
`tests/wl/test_wl_contracts.py::test_wl_labels_are_stable_for_default_graph_input` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-ITERATIONS
+
+Default refinement reaches component fixpoints, explicit rounds remain synchronous, zero is valid and negative counts are refused.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_contracts.py::test_wl_refines_to_a_fixpoint_by_default` (1)
`tests/wl/test_wl_components.py::test_explicit_iterations_still_run_the_requested_global_round_count` (1) |
+| negative | `tests/wl/test_wl_components.py::test_a_negative_iteration_count_is_rejected` (3)
`tests/wl/test_wl_components.py::test_the_error_names_the_argument_and_the_alternatives` (1) |
+| boundary | `tests/wl/test_wl_components.py::test_zero_iterations_is_accepted_and_means_no_refinement` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-BOUNDARY
+
+Embedded triple objects are rejected consistently at zero, explicit and default refinement rounds.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+References: [RDF11-CONCEPTS §3.1 Triples](references/rdf11-concepts.html#section-triples) (normative).
+
+- boundary: [`diffable_rdf.wl:_validate_quad_terms`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_contracts.py::test_wl_functions_leave_supported_input_quads_unchanged` (9) |
+| negative | `tests/wl/test_wl_contracts.py::test_wl_functions_reject_embedded_triple_objects` (24) |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WL-DIRECTION
+
+Dependency-supported directional literals remain distinct and retain language and direction fields during WL relabelling.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/wl/test_wl_contracts.py::test_wl_keeps_direction_tagged_literals_complete_and_distinct` (1) |
+| negative | Not applicable: This row covers dependency-constructible directional literals, not a directional-literal parser. |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WORK-TIME
+
+A representative 8400-triple OWL-shaped graph serializes within the tested process time budget.
+
+Features: `deterministic_turtle`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- dependency: [`diffable_rdf.turtle:deterministic_turtle`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/performance/test_canonicalization_budget.py::test_owl_shaped_graph_canonicalizes_within_a_time_budget` (1) |
+| negative | Not applicable: A representative workload bound is not an API promise to reject all resource-intensive graphs. |
+| boundary | Not applicable: No additional boundary beyond the explicitly listed examples is claimed by this row. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/performance/test_canonicalization_budget.py::test_owl_shaped_graph_canonicalizes_within_a_time_budget` (1) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### WORK-MEMORY
+
+A representative sixteen-round WL workload fits the tested address-space budget where the platform supports limits.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/performance/test_wl_memory_budget.py::test_wl_signatures_stay_bounded_under_many_iterations` (1) |
+| negative | Not applicable: The process resource limit is a test oracle, not a library input-rejection API. |
+| boundary | Not applicable: Platforms unable to set the address-space limit explicitly skip this measurement in pytest. |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | `tests/performance/test_wl_memory_budget.py::test_wl_signatures_stay_bounded_under_many_iterations` (1) |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### PREFIX-MAP
+
+Prefix maps invert the selected RDFLib nonempty bindings into independent string dictionaries.
+
+Features: `well_known_prefix_map`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- dependency: [`diffable_rdf.turtle:well_known_prefix_map`](../../src/diffable_rdf/turtle.py).
+
+| Dimension | Collected evidence or applicability |
+| --- | --- |
+| positive | `tests/contracts/test_prefix_map.py::test_prefix_map_inverts_current_nonempty_bindings` (1)
`tests/integration/test_diffable_rdf.py::test_well_known_prefix_map_contains_schema_org` (1) |
+| negative | Not applicable: The no-argument snapshot function has no invalid input domain. |
+| boundary | `tests/contracts/test_prefix_map.py::test_prefix_map_calls_return_independent_snapshots` (1) |
+| property | Not applicable: No independent randomized property is claimed for this row; examples establish only the stated dimensions. |
+| subprocess | Not applicable: This row has no process-state-specific obligation; process determinism is mapped separately. |
+| end-to-end | Not applicable: This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES. |
+
+### PROFILE-RDFC-PROCESSOR
+
+Standalone RDFC processor conformance, canonical N-Quads bytes and hash selection are outside the wrapper API.
+
+Features: `canonicalize_rdf_graph`.
+
+References: [RDFC10 §2 Conformance](references/rdf-canon.html#conformance) (normative); [RDFC10 §A A Canonical form of N-Quads](references/rdf-canon.html#canonical-quads) (normative); [RDFC10 §3.1 Terms defined by this specification](references/rdf-canon.html#canon-terms) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+Outside this API profile: The wrapper supplies one graph to the dependency and reserializes it; it has no selectable hash algorithm or canonical-byte-output contract.
+
+### PROFILE-RDFC-LIMITS
+
+Configurable RDFC resource-limit reporting is not exposed by the graph serializer API.
+
+Features: `canonicalize_rdf_graph`.
+
+References: [RDFC10 §2 Conformance](references/rdf-canon.html#conformance) (normative).
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+Outside this API profile: Representative workload tests do not expose or establish a standalone processor resource-limit interface.
+
+### PROFILE-JSONLD-PROCESSOR
+
+Full JSON-LD expansion, compaction, remote loading and processing-algorithm conformance are outside the ordering helper.
+
+Features: `deterministic_json`, `canonicalize_rdf_graph`.
+
+References: [JSONLD11-API §3 Conformance](references/json-ld11-api.html#conformance) (normative).
+
+- local: [`diffable_rdf.jsonld:deterministic_json`](../../src/diffable_rdf/jsonld.py).
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+Outside this API profile: The public functions order a JSON tree or serialize an RDF graph; neither is a general JSON-LD processor.
+
+### PROFILE-N3-LOGIC
+
+Notation3 formulas, implications and variable-based logic are outside the supported N3 graph subset.
+
+Features: `canonicalize_rdf_graph`, `format.n3`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- dependency: [`diffable_rdf.canonicalize:canonicalize_rdf_graph`](../../src/diffable_rdf/canonicalize.py).
+
+Outside this API profile: The N3 alias accepts the same single RDF graph as the other serializers and does not provide a logic input model.
+
+### PROFILE-RDF12
+
+Complete RDF 1.2 or embedded-triple conformance is outside the accepted WL term extension.
+
+Features: `wl_blank_node_labels`, `wl_relabel_quads`.
+
+Project contract; no normative standard algorithm is claimed.
+
+- local: [`diffable_rdf.wl:wl_blank_node_labels`](../../src/diffable_rdf/wl.py).
+- local: [`diffable_rdf.wl:wl_relabel_quads`](../../src/diffable_rdf/wl.py).
+
+Outside this API profile: Dependency-supported literal direction is retained, but embedded triples are rejected and no complete RDF 1.2 profile is claimed.
diff --git a/docs/standards/manifest.json b/docs/standards/manifest.json
index 6f42949..7ff51f3 100644
--- a/docs/standards/manifest.json
+++ b/docs/standards/manifest.json
@@ -87,7 +87,33 @@
"W3C-DOCUMENT-RESOLVED"
],
"notice": "Original copyright, publication status and linked document-use or permissive document license notice are retained in the unmodified HTML.",
- "anchors": []
+ "anchors": [
+ {
+ "id": "sec-grammar",
+ "section": "6 Turtle Grammar",
+ "normative": true
+ },
+ {
+ "id": "sec-iri-references",
+ "section": "6.3 IRI References",
+ "normative": true
+ },
+ {
+ "id": "sec-escapes",
+ "section": "6.4 Escape Sequences",
+ "normative": true
+ },
+ {
+ "id": "sec-grammar-grammar",
+ "section": "6.5 Grammar",
+ "normative": true
+ },
+ {
+ "id": "collections",
+ "section": "2.8 Collections",
+ "normative": false
+ }
+ ]
},
{
"id": "TRIG11",
@@ -106,7 +132,13 @@
"W3C-DOCUMENT-RESOLVED"
],
"notice": "Original copyright, publication status and linked document-use or permissive document license notice are retained in the unmodified HTML.",
- "anchors": []
+ "anchors": [
+ {
+ "id": "output-graph",
+ "section": "5.3.1 Output Graph",
+ "normative": true
+ }
+ ]
},
{
"id": "NTRIPLES11",
@@ -125,7 +157,18 @@
"W3C-DOCUMENT-RESOLVED"
],
"notice": "Original copyright, publication status and linked document-use or permissive document license notice are retained in the unmodified HTML.",
- "anchors": []
+ "anchors": [
+ {
+ "id": "sec-iri",
+ "section": "2.2 IRIs",
+ "normative": true
+ },
+ {
+ "id": "n-triples-grammar",
+ "section": "7 Grammar",
+ "normative": true
+ }
+ ]
},
{
"id": "NQUADS11",
@@ -144,7 +187,18 @@
"W3C-DOCUMENT-RESOLVED"
],
"notice": "Original copyright, publication status and linked document-use or permissive document license notice are retained in the unmodified HTML.",
- "anchors": []
+ "anchors": [
+ {
+ "id": "simple-triples",
+ "section": "2.1 Simple Statements",
+ "normative": true
+ },
+ {
+ "id": "sec-grammar",
+ "section": "4 Grammar",
+ "normative": true
+ }
+ ]
},
{
"id": "RDFXML11",
@@ -163,7 +217,13 @@
"W3C-DOCUMENT-RESOLVED"
],
"notice": "Original copyright, publication status and linked document-use or permissive document license notice are retained in the unmodified HTML.",
- "anchors": []
+ "anchors": [
+ {
+ "id": "section-Infoset-Grammar",
+ "section": "7 RDF/XML Grammar",
+ "normative": true
+ }
+ ]
},
{
"id": "JSONLD11",
@@ -196,6 +256,16 @@
"id": "conformance",
"section": "2 Conformance",
"normative": true
+ },
+ {
+ "id": "node-objects",
+ "section": "9.2 Node Objects",
+ "normative": true
+ },
+ {
+ "id": "value-objects",
+ "section": "9.5 Value Objects",
+ "normative": true
}
]
},
@@ -230,6 +300,11 @@
"id": "conformance",
"section": "3 Conformance",
"normative": true
+ },
+ {
+ "id": "iri-expansion",
+ "section": "5.2 IRI Expansion",
+ "normative": true
}
]
},
diff --git a/docs/standards/requirements.json b/docs/standards/requirements.json
new file mode 100644
index 0000000..19c6fc3
--- /dev/null
+++ b/docs/standards/requirements.json
@@ -0,0 +1,2504 @@
+{
+ "schema_version": 1,
+ "features": [
+ {
+ "id": "deterministic_turtle",
+ "kind": "callable",
+ "description": "Diff-stable Turtle graph rendering",
+ "export": "deterministic_turtle"
+ },
+ {
+ "id": "canonicalize_rdf_graph",
+ "kind": "callable",
+ "description": "Single-graph serialization with backend canonical labels",
+ "export": "canonicalize_rdf_graph"
+ },
+ {
+ "id": "deterministic_json",
+ "kind": "callable",
+ "description": "Deterministic JSON ordering with bounded JSON-LD ordering awareness",
+ "export": "deterministic_json"
+ },
+ {
+ "id": "well_known_prefix_map",
+ "kind": "callable",
+ "description": "Independent namespace-to-prefix mappings",
+ "export": "well_known_prefix_map"
+ },
+ {
+ "id": "wl_blank_node_labels",
+ "kind": "callable",
+ "description": "Structural labels for canonicalized quads",
+ "export": "wl_blank_node_labels"
+ },
+ {
+ "id": "wl_relabel_quads",
+ "kind": "callable",
+ "description": "Relabelled copies of canonicalized quads",
+ "export": "wl_relabel_quads"
+ },
+ {
+ "id": "format.turtle",
+ "kind": "format",
+ "description": "Turtle graph output",
+ "aliases": [
+ "turtle",
+ "ttl"
+ ],
+ "backend": "Turtle"
+ },
+ {
+ "id": "format.nt",
+ "kind": "format",
+ "description": "N-Triples graph output",
+ "aliases": [
+ "nt",
+ "ntriples",
+ "n-triples",
+ "nt11"
+ ],
+ "backend": "N-Triples"
+ },
+ {
+ "id": "format.nquads",
+ "kind": "format",
+ "description": "N-Quads graph output",
+ "aliases": [
+ "nquads",
+ "n-quads"
+ ],
+ "backend": "N-Quads"
+ },
+ {
+ "id": "format.xml",
+ "kind": "format",
+ "description": "RDF/XML graph output",
+ "aliases": [
+ "xml",
+ "rdf/xml"
+ ],
+ "backend": "RDF/XML"
+ },
+ {
+ "id": "format.trig",
+ "kind": "format",
+ "description": "TriG graph output",
+ "aliases": [
+ "trig"
+ ],
+ "backend": "TriG"
+ },
+ {
+ "id": "format.n3",
+ "kind": "format",
+ "description": "N3 graph output",
+ "aliases": [
+ "n3"
+ ],
+ "backend": "N3"
+ },
+ {
+ "id": "format.jsonld",
+ "kind": "format",
+ "description": "JSON-LD graph output",
+ "aliases": [
+ "json-ld",
+ "jsonld",
+ "application/ld+json"
+ ],
+ "backend": "JSON-LD"
+ }
+ ],
+ "requirements": [
+ {
+ "id": "API-SURFACE",
+ "description": "Six public functions have the documented exports and resolvable runtime annotations.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "deterministic_json",
+ "well_known_prefix_map",
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:well_known_prefix_map",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/contracts/test_public_api_surface.py::test_the_documented_surface_is_what_the_module_exports",
+ "tests/contracts/test_public_api_surface.py::test_every_public_annotation_resolves_at_runtime"
+ ],
+ "boundary": [
+ "tests/contracts/test_public_api_surface.py::test_the_turtle_entry_point_names_the_graph_type_it_takes",
+ "tests/contracts/test_public_api_surface.py::test_the_preserved_keys_argument_admits_any_set"
+ ],
+ "subprocess": [
+ "tests/contracts/test_public_api_surface.py::test_both_dependencies_are_loaded_before_any_public_call_can_be_made"
+ ]
+ },
+ "not_applicable": {
+ "negative": "Export enumeration and annotation inspection take no caller data to reject.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "API-EXAMPLES",
+ "description": "Eight executable documentation examples run against the selected package; six signature blocks parse as single function definitions.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "deterministic_json",
+ "well_known_prefix_map",
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:well_known_prefix_map",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "end-to-end": [
+ "tests/integration/test_documentation_examples.py::test_documented_example_runs_from_a_neutral_directory"
+ ],
+ "positive": [
+ "tests/integration/test_documentation_examples.py::test_documentation_classifies_every_python_block",
+ "tests/integration/test_documentation_examples.py::test_documented_signature_parses"
+ ],
+ "negative": [
+ "tests/integration/test_documentation_examples.py::test_broken_documentation_example_fails_in_the_selected_interpreter"
+ ],
+ "boundary": [
+ "tests/integration/test_documentation_examples.py::test_extractor_rejects_missing_and_invalid_markers"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately."
+ }
+ },
+ {
+ "id": "GRAPH-ADMISSION",
+ "description": "Graph serializers admit individual graph contexts and reject Dataset and ConjunctiveGraph containers before dispatch.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.1 Triples",
+ "anchor": "section-triples",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.graph_input:_require_single_graph",
+ "path": "src/diffable_rdf/graph_input.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/contracts/test_graph_input.py::test_individual_dataset_graph_context_is_accepted_losslessly",
+ "tests/contracts/test_graph_input.py::test_ordinary_graph_with_context_aware_store_is_accepted"
+ ],
+ "negative": [
+ "tests/contracts/test_graph_input.py::test_dataset_containers_are_rejected_without_mutation",
+ "tests/contracts/test_graph_input.py::test_canonicalize_rejects_datasets_before_every_format_dispatch"
+ ],
+ "boundary": [
+ "tests/contracts/test_graph_input.py::test_canonicalize_rejects_dataset_before_inspecting_format"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "GRAPH-IMMUTABILITY",
+ "description": "Serialization retains caller triples, base, bindings and global literal settings in the checked native and degraded cases.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_literal_fidelity.py::test_serialization_does_not_mutate_input_or_global_normalization",
+ "tests/serialization/test_prefixed_names.py::test_prefixed_names_preserve_every_term_position"
+ ],
+ "boundary": [
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_is_stable_and_does_not_mutate_input_or_global_settings",
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_aliases_are_deterministic_and_leave_input_unchanged"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "FORMAT-DETERMINISM",
+ "description": "Every guaranteed alias has byte-identical output across hash-seeded processes on a shared-list graph.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.nt",
+ "format.nquads",
+ "format.xml",
+ "format.trig",
+ "format.n3",
+ "format.jsonld"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RDFC10",
+ "section": "3.1 Terms defined by this specification",
+ "anchor": "canon-terms",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "subprocess": [
+ "tests/contracts/test_format_guarantee.py::test_every_mapped_format_is_byte_identical_across_processes"
+ ],
+ "positive": [
+ "tests/contracts/test_format_guarantee.py::test_canonicalize_rdf_graph_is_deterministic_across_processes"
+ ]
+ },
+ "not_applicable": {
+ "negative": "Format admission and delegated-name boundaries are mapped separately in FORMAT-DELEGATION.",
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "FORMAT-ALIASES",
+ "description": "Aliases select the same backend syntax; representative mixed-case names and degraded synonyms preserve their contract.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.nt",
+ "format.nquads",
+ "format.xml",
+ "format.trig",
+ "format.n3",
+ "format.jsonld"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/contracts/test_format_guarantee.py::test_mapped_formats_cover_every_name_the_docstring_promises",
+ "tests/contracts/test_format_guarantee.py::test_canonicalize_rdf_graph_accepts_mixed_case_format_names",
+ "tests/serialization/test_degraded_format_coverage.py::test_synonyms_for_one_format_produce_identical_bytes"
+ ],
+ "negative": [
+ "tests/serialization/test_degraded_format_coverage.py::test_synonyms_refuse_for_the_same_reason"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_aliases_are_deterministic_and_leave_input_unchanged",
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_aliases_use_exact_round_trip_guard"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "FORMAT-DELEGATION",
+ "description": "Unmapped plugins are explicitly outside the determinism guarantee; unknown plugins and empty delegated output fail visibly.",
+ "features": [
+ "canonicalize_rdf_graph"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.canonicalize:_deterministic_fallback_serialize",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/contracts/test_format_guarantee.py::test_delegating_to_an_rdflib_plugin_warns_that_the_guarantee_does_not_apply",
+ "tests/contracts/test_format_guarantee.py::test_a_delegated_format_still_gets_canonical_blank_node_labels"
+ ],
+ "negative": [
+ "tests/contracts/test_format_guarantee.py::test_an_unregistered_format_name_still_surfaces_rdflibs_own_error",
+ "tests/serialization/test_fallback_integrity.py::test_a_delegated_format_that_writes_nothing_is_refused_not_returned"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_format_coverage.py::test_an_unregistered_name_still_reaches_rdflib_on_the_degraded_path"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "FORMAT-FRAMING",
+ "description": "Nonempty mapped RDF output has exactly one trailing newline on normal and carrying degraded paths.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.nt",
+ "format.nquads",
+ "format.xml",
+ "format.trig",
+ "format.n3",
+ "format.jsonld"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:_with_single_trailing_newline",
+ "path": "src/diffable_rdf/canonicalize.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/contracts/test_trailing_newline.py::test_every_mapped_format_ends_with_exactly_one_newline",
+ "tests/contracts/test_trailing_newline.py::test_deterministic_turtle_ends_with_exactly_one_newline"
+ ],
+ "boundary": [
+ "tests/contracts/test_trailing_newline.py::test_the_degraded_path_ends_with_exactly_one_newline",
+ "tests/contracts/test_trailing_newline.py::test_deterministic_turtle_ends_with_exactly_one_newline_when_degraded",
+ "tests/contracts/test_trailing_newline.py::test_normalising_the_newline_does_not_add_a_blank_line"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "FORMAT-EMPTY",
+ "description": "Empty Turtle and N-Triples stay empty; empty JSON-LD is a newline-terminated empty array.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.nt",
+ "format.jsonld"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "boundary": [
+ "tests/contracts/test_trailing_newline.py::test_an_empty_graph_stays_an_empty_document"
+ ],
+ "positive": [
+ "tests/contracts/test_trailing_newline.py::test_an_empty_graph_stays_an_empty_document"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "GRAPH-DEFAULT",
+ "description": "Single-graph N-Quads has no graph label; degraded TriG carries explicit default-graph list triples.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.nquads",
+ "format.trig"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "NQUADS11",
+ "section": "2.1 Simple Statements",
+ "anchor": "simple-triples",
+ "normative": true
+ },
+ {
+ "reference": "TRIG11",
+ "section": "5.3.1 Output Graph",
+ "anchor": "output-graph",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_format_coverage.py::test_line_oriented_output_still_works_when_the_graph_is_representable",
+ "tests/serialization/test_degraded_format_coverage.py::test_degraded_trig_states_list_structure_explicitly"
+ ],
+ "negative": [
+ "tests/contracts/test_graph_input.py::test_canonicalize_rejects_datasets_before_every_format_dispatch"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_format_coverage.py::test_degraded_output_preserves_the_graph"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "RDF-ISOMORPHISM",
+ "description": "Seeded Turtle graph shapes preserve RDF graph identity after serialization and parsing.",
+ "features": [
+ "deterministic_turtle"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.6 Graph Comparison",
+ "anchor": "section-graph-equality",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "property": [
+ "tests/properties/test_canonicalization_properties.py::test_p1_canonical_output_is_lossless"
+ ],
+ "positive": [
+ "tests/properties/test_canonicalization_properties.py::test_p1_canonical_output_is_lossless"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "TURTLE-STABILITY",
+ "description": "Seeded Turtle output is idempotent and independent of insertion order and source blank-node names.",
+ "features": [
+ "deterministic_turtle"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "property": [
+ "tests/properties/test_canonicalization_properties.py::test_p2_canonicalization_is_idempotent",
+ "tests/properties/test_canonicalization_properties.py::test_p3_output_does_not_depend_on_blank_node_labels",
+ "tests/properties/test_canonicalization_properties.py::test_p4_output_does_not_depend_on_insertion_order"
+ ],
+ "positive": [
+ "tests/properties/test_canonicalization_properties.py::test_p2_canonicalization_is_idempotent"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "RDF-LEXICAL",
+ "description": "Checked integer, boolean, dateTime, decimal, signed-zero and non-finite literal spellings retain lexical term identity.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.trig",
+ "format.n3"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.3 Literals",
+ "anchor": "section-Graph-Literal",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_distinct_lexical_forms",
+ "tests/serialization/test_literal_fidelity.py::test_lower_level_turtle_family_preserves_literal_identity"
+ ],
+ "boundary": [
+ "tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_full_double_precision",
+ "tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_other_typed_lexical_forms",
+ "tests/serialization/test_numeric_literal_edges.py::test_the_lexical_form_the_graph_holds_is_what_is_written"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "RDF-LANGUAGE",
+ "description": "Plain strings and xsd:string render equivalently; normal-path language tags use the permitted lowercase spelling.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.3 Literals",
+ "anchor": "section-Graph-Literal",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_literal_fidelity.py::test_intentional_string_and_language_equivalences_remain_canonical",
+ "tests/integration/test_diffable_rdf.py::test_canonicalize_rdf_graph_normalizes_language_tag_case"
+ ],
+ "boundary": [
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_accepts_plain_string_and_xsd_string_datatype_equivalence",
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_keeps_a_direct_xsd_string_iri_significant"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "RDF-IRI-BASE",
+ "description": "Fragment, path, query and equal-base namespaces preserve direct and datatype IRIs in the checked verified renderings.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.trig",
+ "format.n3",
+ "format.xml"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "TURTLE11",
+ "section": "6.3 IRI References",
+ "anchor": "sec-iri-references",
+ "normative": true
+ },
+ {
+ "reference": "RFC3986",
+ "section": "5.2 Relative Resolution",
+ "anchor": null,
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ },
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_base_iri_fidelity.py::test_a_fragment_base_preserves_the_terms",
+ "tests/serialization/test_base_iri_fidelity.py::test_an_equal_base_namespace_preserves_every_iri_position"
+ ],
+ "boundary": [
+ "tests/serialization/test_base_iri_fidelity.py::test_deterministic_turtle_preserves_hash_path_and_slash_bases",
+ "tests/serialization/test_base_iri_fidelity.py::test_an_unprefixed_query_base_retries_without_base_and_preserves_datatypes",
+ "tests/serialization/test_base_iri_fidelity.py::test_a_datatype_only_query_base_retries_without_base",
+ "tests/serialization/test_base_iri_fidelity.py::test_rdf_xml_retries_without_base_for_typed_literal_namespaces"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "RDF-DATATYPE-GUARD",
+ "description": "Round-trip boundary checks distinguish missing or invented datatype IRIs from xsd:string literal equivalence.",
+ "features": [
+ "canonicalize_rdf_graph"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.3 Literals",
+ "anchor": "section-Graph-Literal",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.canonicalize:_assert_round_trips",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_accepts_plain_string_and_xsd_string_datatype_equivalence"
+ ],
+ "negative": [
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_catches_missing_and_invented_literal_datatypes",
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_catches_an_iri_the_input_never_contained",
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_keeps_a_direct_xsd_string_iri_significant"
+ ],
+ "boundary": [
+ "tests/serialization/test_base_iri_fidelity.py::test_the_guard_tolerates_rdflibs_literal_normalization"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "TURTLE-QUOTING",
+ "description": "Quoted literals preserve terminal quote/backslash runs, LF, CR and CRLF in checked native and relative-subject outputs.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.trig",
+ "format.n3"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "TURTLE11",
+ "section": "6.4 Escape Sequences",
+ "anchor": "sec-escapes",
+ "normative": true
+ },
+ {
+ "reference": "TURTLE11",
+ "section": "6.5 Grammar",
+ "anchor": "sec-grammar-grammar",
+ "normative": true
+ },
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.3 Literals",
+ "anchor": "section-Graph-Literal",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_numeric_literal_edges.py::test_literals_needing_escapes_still_round_trip"
+ ],
+ "boundary": [
+ "tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_preserves_literal_quote_boundaries",
+ "tests/serialization/test_literal_fidelity.py::test_turtle_aliases_preserve_literal_quote_boundaries",
+ "tests/serialization/test_literal_fidelity.py::test_deterministic_turtle_fallback_preserves_literal_quote_boundaries",
+ "tests/serialization/test_literal_fidelity.py::test_turtle_alias_fallbacks_preserve_literal_quote_boundaries"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "TURTLE-NAMES",
+ "description": "Punctuation, Unicode-prefix and overlapping-prefix examples preserve subject, predicate, object and datatype IRIs.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.trig",
+ "format.n3"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "TURTLE11",
+ "section": "6.5 Grammar",
+ "anchor": "sec-grammar-grammar",
+ "normative": true
+ },
+ {
+ "reference": "RFC3987",
+ "section": "2.2 ABNF for IRI References and IRIs",
+ "anchor": null,
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ },
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_prefixed_names.py::test_prefixed_names_preserve_every_term_position",
+ "tests/serialization/test_prefixed_names.py::test_overlapping_prefixes_and_aliases_remain_optional"
+ ],
+ "boundary": [
+ "tests/serialization/test_prefixed_names.py::test_curie_looking_literal_content_is_data",
+ "tests/serialization/test_prefixed_names.py::test_deterministic_turtle_preserves_curie_looking_literal_content"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "TURTLE-COLLECTIONS",
+ "description": "Private, shared-head and shared-tail list examples retain cell identity; the degraded path uses explicit list triples.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph",
+ "format.turtle",
+ "format.trig",
+ "format.n3"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.6 Graph Comparison",
+ "anchor": "section-graph-equality",
+ "normative": true
+ },
+ {
+ "reference": "TURTLE11",
+ "section": "2.8 Collections",
+ "anchor": "collections",
+ "normative": false
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ },
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/properties/test_canonicalization_properties.py::test_shared_list_structures_survive_canonicalization"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_collections.py::test_degraded_turtle_is_lossless_for_shared_list_tails",
+ "tests/serialization/test_degraded_collections.py::test_degraded_n3_is_lossless_for_shared_list_tails",
+ "tests/serialization/test_degraded_format_coverage.py::test_degraded_trig_states_list_structure_explicitly"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "TURTLE-PRESENTATION",
+ "description": "Diff-stable Turtle uses quoted typed literals and inline blank nodes where the graph shape permits.",
+ "features": [
+ "deterministic_turtle"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "TURTLE11",
+ "section": "6.5 Grammar",
+ "anchor": "sec-grammar-grammar",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_literal_fidelity.py::test_typed_literals_are_written_in_quoted_form",
+ "tests/integration/test_diffable_rdf.py::test_deterministic_turtle_uses_inline_blank_nodes"
+ ],
+ "boundary": [
+ "tests/serialization/test_numeric_literal_edges.py::test_the_object_order_is_total_for_value_tied_and_mixed_terms"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "NAMESPACES-ISOLATION",
+ "description": "Namespace selection preserves caller bindings, reserves generated names and remains stable across processes in checked graph shapes.",
+ "features": [
+ "deterministic_turtle",
+ "canonicalize_rdf_graph"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.namespaces:prepare_namespaces",
+ "path": "src/diffable_rdf/namespaces.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_namespace_determinism.py::test_caller_prefixes_are_preserved_and_generated_names_are_reserved"
+ ],
+ "negative": [
+ "tests/serialization/test_namespace_determinism.py::test_an_undeclarable_binding_preserves_usable_prefixes"
+ ],
+ "boundary": [
+ "tests/serialization/test_namespace_determinism.py::test_deterministic_turtle_degraded_path_keeps_graph_and_bindings",
+ "tests/serialization/test_namespace_determinism.py::test_the_prefix_filter_matches_what_pyoxigraph_will_accept"
+ ],
+ "subprocess": [
+ "tests/serialization/test_namespace_determinism.py::test_namespace_allocation_is_stable_across_processes"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "SERIALIZER-VERIFICATION",
+ "description": "Verified renderings remove base then prefixes only after failed fidelity checks; final validation and unrelated backend errors propagate.",
+ "features": [
+ "canonicalize_rdf_graph"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_base_iri_fidelity.py::test_a_verified_base_does_not_serialize_twice",
+ "tests/serialization/test_prefixed_names.py::test_a_valid_compact_spelling_keeps_a_problematic_binding"
+ ],
+ "negative": [
+ "tests/serialization/test_prefixed_names.py::test_final_prefix_attempt_propagates_validation_failure",
+ "tests/serialization/test_base_iri_fidelity.py::test_an_unrelated_backend_error_is_not_retried",
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_guard_rejects_corrupted_serializer_output"
+ ],
+ "boundary": [
+ "tests/serialization/test_prefixed_names.py::test_verification_failures_remove_options_in_order",
+ "tests/serialization/test_prefixed_names.py::test_retry_options_do_not_affect_later_renderings"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "LINE-TERMS",
+ "description": "Line syntaxes refuse relative or malformed IRIs and name the offending term position rather than emit invalid N-Triples or N-Quads.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.nt",
+ "format.nquads"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "NTRIPLES11",
+ "section": "2.2 IRIs",
+ "anchor": "sec-iri",
+ "normative": true
+ },
+ {
+ "reference": "NTRIPLES11",
+ "section": "7 Grammar",
+ "anchor": "n-triples-grammar",
+ "normative": true
+ },
+ {
+ "reference": "NQUADS11",
+ "section": "4 Grammar",
+ "anchor": "sec-grammar",
+ "normative": true
+ },
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.1 Triples",
+ "anchor": "section-triples",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.canonicalize:_first_term_n_triples_cannot_write",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_format_coverage.py::test_line_oriented_output_still_works_when_the_graph_is_representable"
+ ],
+ "negative": [
+ "tests/serialization/test_degraded_format_coverage.py::test_line_oriented_aliases_refuse_a_graph_they_cannot_represent",
+ "tests/serialization/test_degraded_format_coverage.py::test_the_refusal_covers_an_iri_that_is_absolute_but_not_valid"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_format_coverage.py::test_the_refusal_names_the_position_of_the_offending_term"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "LINE-SEPARATORS",
+ "description": "The internal line sorter preserves Unicode separators inside literals and sorts complete statement lines.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.nt",
+ "format.nquads"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "NTRIPLES11",
+ "section": "7 Grammar",
+ "anchor": "n-triples-grammar",
+ "normative": true
+ },
+ {
+ "reference": "NQUADS11",
+ "section": "4 Grammar",
+ "anchor": "sec-grammar",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:_deterministic_fallback_serialize",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_fallback_integrity.py::test_the_line_sort_still_sorts"
+ ],
+ "boundary": [
+ "tests/serialization/test_fallback_integrity.py::test_the_line_sort_preserves_a_literal_carrying_a_unicode_break"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This internal branch is tested directly; unrepresentable public line-format inputs are refused before it."
+ }
+ },
+ {
+ "id": "XML-LITERALS",
+ "description": "Checked RDF/XML literals retain CR, CRLF, tabs, Unicode, markup escapes, datatypes and language identity after parsing.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.xml"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "XML10",
+ "section": "2.11 End-of-Line Handling",
+ "anchor": "sec-line-ends",
+ "normative": true
+ },
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.3 Literals",
+ "anchor": "section-Graph-Literal",
+ "normative": true
+ },
+ {
+ "reference": "RDFXML11",
+ "section": "7 RDF/XML Grammar",
+ "anchor": "section-Infoset-Grammar",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.canonicalize:_finalize_rdf_xml",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_preserves_diverse_literal_content_and_identity"
+ ],
+ "boundary": [
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_preserves_known_lossy_literal_values",
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_aliases_work_on_degraded_fallback",
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_a_literal_carriage_return_still_survives_the_sort"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "XML-CHARACTERS",
+ "description": "RDF/XML rejects XML 1.0-forbidden characters in literal content and serializer metadata.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.xml"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "XML10",
+ "section": "2.2 Characters",
+ "anchor": "charsets",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.canonicalize:_assert_xml_10_representable",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_preserves_diverse_literal_content_and_identity"
+ ],
+ "negative": [
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_rejects_characters_forbidden_by_xml_10"
+ ],
+ "boundary": [
+ "tests/serialization/test_xml_fidelity.py::test_rdf_xml_rejects_forbidden_characters_in_serializer_metadata"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "XML-NAMESPACE",
+ "description": "Checked RDF/XML namespace declarations survive sorting; an inexpressible generalized literal predicate surfaces its backend QName error.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.xml"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "XMLNS10",
+ "section": "4 Qualified Names",
+ "anchor": "ns-qualnames",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_the_callers_prefix_names_survive_the_sort",
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_the_document_stays_well_formed_and_declared"
+ ],
+ "negative": [
+ "tests/serialization/test_fallback_integrity.py::test_a_fallback_serializer_error_names_the_format_and_a_way_forward"
+ ]
+ },
+ "not_applicable": {
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "XML-ORDERING",
+ "description": "Degraded RDF/XML orders descriptions and property elements without altering graph meaning or caller prefixes.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.xml"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.canonicalize:_sort_rdf_xml_descriptions",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_sorting_preserves_the_graph",
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_property_elements_within_a_subject_are_ordered_too",
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_properties_are_ordered_by_predicate_not_by_their_objects_label"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_the_output_does_not_depend_on_process_global_xml_state"
+ ],
+ "subprocess": [
+ "tests/serialization/test_degraded_rdfxml_determinism.py::test_degraded_rdf_xml_is_byte_identical_across_processes"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSONLD-GRAPH",
+ "description": "The normal expanded JSON-LD backend preserves shared RDF list cells instead of compacting them into duplicate lists.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.jsonld"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "JSONLD11",
+ "section": "9.2 Node Objects",
+ "anchor": "node-objects",
+ "normative": true
+ },
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.6 Graph Comparison",
+ "anchor": "section-graph-equality",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_collections.py::test_json_ld_is_lossless_for_shared_list_tails"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_jsonld.py::test_normal_json_ld_path_still_uses_pyoxigraph"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSONLD-FALLBACK",
+ "description": "Expanded degraded JSON-LD retains shared cells, references, cycles and literal annotation strings.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.jsonld"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "JSONLD11",
+ "section": "9.2 Node Objects",
+ "anchor": "node-objects",
+ "normative": true
+ },
+ {
+ "reference": "JSONLD11",
+ "section": "9.5 Value Objects",
+ "anchor": "value-objects",
+ "normative": true
+ },
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.6 Graph Comparison",
+ "anchor": "section-graph-equality",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.expanded_jsonld:serialize_expanded_jsonld",
+ "path": "src/diffable_rdf/expanded_jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_shared_cells_references_and_cycles",
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_literal_lexical_strings_and_annotations"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_nonkeyword_identifiers_with_an_explicit_base"
+ ],
+ "subprocess": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_is_reproducible_across_processes"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSONLD-TERM-BOUNDARY",
+ "description": "The degraded JSON-LD subset refuses generalized term positions, relative predicate/datatype IRIs and URIRef values resembling blank-node identifiers.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.jsonld"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.1 Triples",
+ "anchor": "section-triples",
+ "normative": true
+ },
+ {
+ "reference": "JSONLD11",
+ "section": "9.2 Node Objects",
+ "anchor": "node-objects",
+ "normative": true
+ },
+ {
+ "reference": "JSONLD11",
+ "section": "9.5 Value Objects",
+ "anchor": "value-objects",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.expanded_jsonld:serialize_expanded_jsonld",
+ "path": "src/diffable_rdf/expanded_jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_literal_lexical_strings_and_annotations"
+ ],
+ "negative": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_rejects_terms_outside_interoperable_subset",
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_rejects_literal_predicates"
+ ]
+ },
+ "not_applicable": {
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSONLD-RESERVED",
+ "description": "The degraded writer refuses keyword-shaped relative identifiers, while keyword-shaped literal text and non-keyword identifier spellings survive.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.jsonld"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "JSONLD11-API",
+ "section": "5.2 IRI Expansion",
+ "anchor": "iri-expansion",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.expanded_jsonld:_iri_identifier",
+ "path": "src/diffable_rdf/expanded_jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "negative": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_rejects_reserved_relative_identifiers"
+ ],
+ "positive": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_keyword_shaped_literal_text"
+ ],
+ "boundary": [
+ "tests/serialization/test_degraded_jsonld.py::test_degraded_json_ld_preserves_nonkeyword_identifiers_with_an_explicit_base"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-ORDERING",
+ "description": "Object keys and application-unordered arrays sort deterministically without mutation; this changes ordinary JSON array order by project policy.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RFC8259",
+ "section": "4 Objects",
+ "anchor": null,
+ "normative": true
+ },
+ {
+ "reference": "RFC8259",
+ "section": "5 Arrays",
+ "anchor": null,
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/integration/test_diffable_rdf.py::test_deterministic_json_sorts_keys_and_lists",
+ "tests/json/test_jsonld_order.py::test_context_free_json_still_sorts_lists_and_does_not_mutate_input"
+ ],
+ "property": [
+ "tests/json/test_json_mixed_keys.py::test_parsed_json_rendering_is_idempotent",
+ "tests/integration/test_diffable_rdf.py::test_deterministic_json_is_stable_regardless_of_insertion_order"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-MIXEDKEYS",
+ "description": "Mixed Python keys sort by their encoded names, and duplicate encoded names are refused before JSON object information can be lost.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "extension",
+ "clauses": [
+ {
+ "reference": "RFC8259",
+ "section": "4 Objects",
+ "anchor": null,
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:_json_key",
+ "path": "src/diffable_rdf/jsonld.py"
+ },
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.jsonld:_sorted_items",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/json/test_json_mixed_keys.py::test_mixed_key_dictionary_list_permutations_are_deterministic"
+ ],
+ "negative": [
+ "tests/json/test_json_mixed_keys.py::test_encoded_key_collisions_are_refused_not_emitted_twice",
+ "tests/json/test_json_mixed_keys.py::test_two_distinct_nan_keys_are_refused_as_well"
+ ],
+ "boundary": [
+ "tests/json/test_json_mixed_keys.py::test_non_string_keys_sort_by_the_name_json_writes_not_by_str"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-LISTS",
+ "description": "Explicit and locally declared lists retain order, including nested lists, while graph/set arrays may be reordered without changing the checked RDF meaning.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "JSONLD11",
+ "section": "9.7 Lists and Sets",
+ "anchor": "lists-and-sets",
+ "normative": true
+ },
+ {
+ "reference": "JSONLD11",
+ "section": "4.3 Value Ordering",
+ "anchor": "sets-and-lists",
+ "normative": false
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/json/test_jsonld_order.py::test_context_defined_list_preserves_rdf_meaning",
+ "tests/json/test_jsonld_order.py::test_explicit_list_preserves_rdf_meaning",
+ "tests/json/test_jsonld_unordered_keys.py::test_sorting_an_unordered_container_does_not_change_the_rdf"
+ ],
+ "boundary": [
+ "tests/json/test_jsonld_order.py::test_an_array_nested_in_an_ordered_array_keeps_its_order",
+ "tests/json/test_jsonld_unordered_keys.py::test_a_container_list_term_nested_in_a_set_array_keeps_its_order",
+ "tests/json/test_jsonld_unordered_keys.py::test_an_aliased_list_nested_in_a_graph_array_keeps_its_order",
+ "tests/json/test_jsonld_order.py::test_a_dict_inside_an_ordered_array_still_starts_a_sortable_node_object"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-CONTEXTS",
+ "description": "Local ordering facts respect sequential context overrides, inheritance, null resets and checked forward alias definitions without claiming a full context processor.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "JSONLD11-API",
+ "section": "4.1 Context Processing Algorithm",
+ "anchor": "context-processing-algorithm",
+ "normative": true
+ },
+ {
+ "reference": "JSONLD11-API",
+ "section": "4.2 Create Term Definition",
+ "anchor": "create-term-definition",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:_apply_local_context",
+ "path": "src/diffable_rdf/jsonld.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:_apply_term_definitions",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/json/test_jsonld_order.py::test_context_overrides_are_applied_in_order",
+ "tests/json/test_jsonld_order.py::test_local_context_is_inherited_and_null_resets_it",
+ "tests/json/test_jsonld_order.py::test_list_keyword_alias_preserves_rdf_meaning"
+ ],
+ "boundary": [
+ "tests/json/test_jsonld_order.py::test_a_term_definition_may_reference_an_alias_declared_after_it",
+ "tests/json/test_jsonld_order.py::test_a_shadowing_definition_in_a_nested_context_array_keeps_its_order",
+ "tests/json/test_jsonld_unordered_keys.py::test_a_context_inside_a_graph_element_is_still_applied"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-LITERALS",
+ "description": "Explicit and locally recognized JSON literal payload arrays retain their sequence and checked RDF meaning.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "JSONLD11",
+ "section": "9.5 Value Objects",
+ "anchor": "value-objects",
+ "normative": true
+ },
+ {
+ "reference": "RFC8259",
+ "section": "5 Arrays",
+ "anchor": null,
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/json/test_jsonld_order.py::test_json_literal_arrays_preserve_rdf_meaning"
+ ],
+ "boundary": [
+ "tests/json/test_jsonld_order.py::test_a_term_definition_may_reference_an_alias_declared_after_it[type-json-through-an-alias-declared-later]"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-UNKNOWN",
+ "description": "Unknown remote, scoped and unsupported local contexts conservatively preserve descendant arrays and never fetch remote contexts.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:_apply_local_context",
+ "path": "src/diffable_rdf/jsonld.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/json/test_jsonld_order.py::test_scoped_context_fails_safe_for_the_document",
+ "tests/json/test_jsonld_order.py::test_type_scoped_context_fails_safe_and_preserves_rdf_meaning"
+ ],
+ "negative": [
+ "tests/json/test_jsonld_order.py::test_remote_context_is_never_loaded_and_fails_safe"
+ ],
+ "boundary": [
+ "tests/json/test_jsonld_order.py::test_unsupported_context_directive_fails_safe"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "JSON-OVERRIDES",
+ "description": "Custom preserved-key sets replace convenience defaults without disabling keyword-based list protection.",
+ "features": [
+ "deterministic_json"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "JSONLD11",
+ "section": "9.7 Lists and Sets",
+ "anchor": "lists-and-sets",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/json/test_jsonld_unordered_keys.py::test_a_custom_key_set_replaces_the_default_but_not_the_keyword_rules",
+ "tests/contracts/test_public_api_surface.py::test_the_preserved_keys_argument_admits_any_set"
+ ],
+ "boundary": [
+ "tests/json/test_jsonld_unordered_keys.py::test_imports_stays_protected_by_default_and_is_droppable",
+ "tests/json/test_json_mixed_keys.py::test_custom_preserved_list_keeps_mixed_key_element_order"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-IDENTITY",
+ "description": "WL relabelling returns a fresh isomorphic quad list, preserves blank-node cardinality and leaves input quads untouched.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.6 Graph Comparison",
+ "anchor": "section-graph-equality",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_contracts.py::test_wl_relabel_quads_preserves_the_graph"
+ ],
+ "boundary": [
+ "tests/wl/test_wl_contracts.py::test_wl_functions_leave_supported_input_quads_unchanged"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-COMPONENTS",
+ "description": "Unrelated additions, edits and removals do not advance a disconnected component's default refinement.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads",
+ "deterministic_turtle"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_components.py::test_default_convergence_isolated_from_disconnected_changes",
+ "tests/wl/test_wl_components.py::test_public_turtle_preserves_a_visible_disconnected_label",
+ "tests/wl/test_wl_contracts.py::test_wl_relabel_quads_is_diff_stable"
+ ],
+ "property": [
+ "tests/wl/test_wl_components.py::test_component_labels_ignore_input_order_and_blank_node_names"
+ ],
+ "boundary": [
+ "tests/wl/test_wl_components.py::test_shared_blank_subject_object_roles_form_one_component",
+ "tests/wl/test_wl_components.py::test_graph_membership_does_not_join_signature_independent_components"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-TIES",
+ "description": "Structurally tied nodes remain injective and collision suffixes follow numeric canonical numbering.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_contracts.py::test_wl_blank_node_labels_are_injective",
+ "tests/wl/test_wl_components.py::test_tied_components_remain_injective"
+ ],
+ "boundary": [
+ "tests/wl/test_wl_components.py::test_the_tie_break_follows_the_canonical_numbering",
+ "tests/wl/test_wl_components.py::test_adding_a_blank_node_does_not_relabel_the_tied_ones_before_it"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-GRAPHS",
+ "description": "Named graphs influence labels; blank graph-name identifiers remain consistent when also used as object terms.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_contracts.py::test_wl_distinguishes_identical_structure_in_different_named_graphs",
+ "tests/wl/test_wl_contracts.py::test_wl_relabel_quads_remaps_blank_node_graph_names"
+ ],
+ "boundary": [
+ "tests/wl/test_wl_contracts.py::test_wl_labels_graph_name_only_blank_nodes_by_content",
+ "tests/wl/test_wl_contracts.py::test_wl_labels_are_stable_for_default_graph_input"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This is a transformation of admitted values, not a syntax parser or a new input-rejection API.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-ITERATIONS",
+ "description": "Default refinement reaches component fixpoints, explicit rounds remain synchronous, zero is valid and negative counts are refused.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_contracts.py::test_wl_refines_to_a_fixpoint_by_default",
+ "tests/wl/test_wl_components.py::test_explicit_iterations_still_run_the_requested_global_round_count"
+ ],
+ "negative": [
+ "tests/wl/test_wl_components.py::test_a_negative_iteration_count_is_rejected",
+ "tests/wl/test_wl_components.py::test_the_error_names_the_argument_and_the_alternatives"
+ ],
+ "boundary": [
+ "tests/wl/test_wl_components.py::test_zero_iterations_is_accepted_and_means_no_refinement"
+ ]
+ },
+ "not_applicable": {
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-BOUNDARY",
+ "description": "Embedded triple objects are rejected consistently at zero, explicit and default refinement rounds.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [
+ {
+ "reference": "RDF11-CONCEPTS",
+ "section": "3.1 Triples",
+ "anchor": "section-triples",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "boundary",
+ "target": "diffable_rdf.wl:_validate_quad_terms",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_contracts.py::test_wl_functions_leave_supported_input_quads_unchanged"
+ ],
+ "negative": [
+ "tests/wl/test_wl_contracts.py::test_wl_functions_reject_embedded_triple_objects"
+ ]
+ },
+ "not_applicable": {
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WL-DIRECTION",
+ "description": "Dependency-supported directional literals remain distinct and retain language and direction fields during WL relabelling.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "extension",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/wl/test_wl_contracts.py::test_wl_keeps_direction_tagged_literals_complete_and_distinct"
+ ]
+ },
+ "not_applicable": {
+ "negative": "This row covers dependency-constructible directional literals, not a directional-literal parser.",
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WORK-TIME",
+ "description": "A representative 8400-triple OWL-shaped graph serializes within the tested process time budget.",
+ "features": [
+ "deterministic_turtle"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:deterministic_turtle",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "subprocess": [
+ "tests/performance/test_canonicalization_budget.py::test_owl_shaped_graph_canonicalizes_within_a_time_budget"
+ ],
+ "positive": [
+ "tests/performance/test_canonicalization_budget.py::test_owl_shaped_graph_canonicalizes_within_a_time_budget"
+ ]
+ },
+ "not_applicable": {
+ "negative": "A representative workload bound is not an API promise to reject all resource-intensive graphs.",
+ "boundary": "No additional boundary beyond the explicitly listed examples is claimed by this row.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "WORK-MEMORY",
+ "description": "A representative sixteen-round WL workload fits the tested address-space budget where the platform supports limits.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "subprocess": [
+ "tests/performance/test_wl_memory_budget.py::test_wl_signatures_stay_bounded_under_many_iterations"
+ ],
+ "positive": [
+ "tests/performance/test_wl_memory_budget.py::test_wl_signatures_stay_bounded_under_many_iterations"
+ ]
+ },
+ "not_applicable": {
+ "negative": "The process resource limit is a test oracle, not a library input-rejection API.",
+ "boundary": "Platforms unable to set the address-space limit explicitly skip this measurement in pytest.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "PREFIX-MAP",
+ "description": "Prefix maps invert the selected RDFLib nonempty bindings into independent string dictionaries.",
+ "features": [
+ "well_known_prefix_map"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.turtle:well_known_prefix_map",
+ "path": "src/diffable_rdf/turtle.py"
+ }
+ ],
+ "status": "supported",
+ "reason": null,
+ "evidence": {
+ "positive": [
+ "tests/contracts/test_prefix_map.py::test_prefix_map_inverts_current_nonempty_bindings",
+ "tests/integration/test_diffable_rdf.py::test_well_known_prefix_map_contains_schema_org"
+ ],
+ "boundary": [
+ "tests/contracts/test_prefix_map.py::test_prefix_map_calls_return_independent_snapshots"
+ ]
+ },
+ "not_applicable": {
+ "negative": "The no-argument snapshot function has no invalid input domain.",
+ "property": "No independent randomized property is claimed for this row; examples establish only the stated dimensions.",
+ "subprocess": "This row has no process-state-specific obligation; process determinism is mapped separately.",
+ "end-to-end": "This row isolates a contract dimension; executable composed workflows are mapped in API-EXAMPLES."
+ }
+ },
+ {
+ "id": "PROFILE-RDFC-PROCESSOR",
+ "description": "Standalone RDFC processor conformance, canonical N-Quads bytes and hash selection are outside the wrapper API.",
+ "features": [
+ "canonicalize_rdf_graph"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "RDFC10",
+ "section": "2 Conformance",
+ "anchor": "conformance",
+ "normative": true
+ },
+ {
+ "reference": "RDFC10",
+ "section": "A A Canonical form of N-Quads",
+ "anchor": "canonical-quads",
+ "normative": true
+ },
+ {
+ "reference": "RDFC10",
+ "section": "3.1 Terms defined by this specification",
+ "anchor": "canon-terms",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "out-of-profile",
+ "reason": "The wrapper supplies one graph to the dependency and reserializes it; it has no selectable hash algorithm or canonical-byte-output contract.",
+ "evidence": {},
+ "not_applicable": {}
+ },
+ {
+ "id": "PROFILE-RDFC-LIMITS",
+ "description": "Configurable RDFC resource-limit reporting is not exposed by the graph serializer API.",
+ "features": [
+ "canonicalize_rdf_graph"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "RDFC10",
+ "section": "2 Conformance",
+ "anchor": "conformance",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "out-of-profile",
+ "reason": "Representative workload tests do not expose or establish a standalone processor resource-limit interface.",
+ "evidence": {},
+ "not_applicable": {}
+ },
+ {
+ "id": "PROFILE-JSONLD-PROCESSOR",
+ "description": "Full JSON-LD expansion, compaction, remote loading and processing-algorithm conformance are outside the ordering helper.",
+ "features": [
+ "deterministic_json",
+ "canonicalize_rdf_graph"
+ ],
+ "category": "normative",
+ "clauses": [
+ {
+ "reference": "JSONLD11-API",
+ "section": "3 Conformance",
+ "anchor": "conformance",
+ "normative": true
+ }
+ ],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.jsonld:deterministic_json",
+ "path": "src/diffable_rdf/jsonld.py"
+ },
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "out-of-profile",
+ "reason": "The public functions order a JSON tree or serialize an RDF graph; neither is a general JSON-LD processor.",
+ "evidence": {},
+ "not_applicable": {}
+ },
+ {
+ "id": "PROFILE-N3-LOGIC",
+ "description": "Notation3 formulas, implications and variable-based logic are outside the supported N3 graph subset.",
+ "features": [
+ "canonicalize_rdf_graph",
+ "format.n3"
+ ],
+ "category": "policy",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "dependency",
+ "target": "diffable_rdf.canonicalize:canonicalize_rdf_graph",
+ "path": "src/diffable_rdf/canonicalize.py"
+ }
+ ],
+ "status": "out-of-profile",
+ "reason": "The N3 alias accepts the same single RDF graph as the other serializers and does not provide a logic input model.",
+ "evidence": {},
+ "not_applicable": {}
+ },
+ {
+ "id": "PROFILE-RDF12",
+ "description": "Complete RDF 1.2 or embedded-triple conformance is outside the accepted WL term extension.",
+ "features": [
+ "wl_blank_node_labels",
+ "wl_relabel_quads"
+ ],
+ "category": "extension",
+ "clauses": [],
+ "owners": [
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_blank_node_labels",
+ "path": "src/diffable_rdf/wl.py"
+ },
+ {
+ "role": "local",
+ "target": "diffable_rdf.wl:wl_relabel_quads",
+ "path": "src/diffable_rdf/wl.py"
+ }
+ ],
+ "status": "out-of-profile",
+ "reason": "Dependency-supported literal direction is retained, but embedded triples are rejected and no complete RDF 1.2 profile is claimed.",
+ "evidence": {},
+ "not_applicable": {}
+ }
+ ]
+}
diff --git a/scripts/check_requirements.py b/scripts/check_requirements.py
new file mode 100644
index 0000000..00a4aa7
--- /dev/null
+++ b/scripts/check_requirements.py
@@ -0,0 +1,471 @@
+#!/usr/bin/env python3
+"""Check scoped requirements against pinned clauses and collected pytest evidence."""
+
+from __future__ import annotations
+
+import argparse
+import importlib.util
+import inspect
+import json
+import os
+from pathlib import Path
+import re
+import subprocess
+import sys
+import tempfile
+from typing import Any, Sequence
+
+
+_ROOT = Path(__file__).resolve().parents[1]
+_CATALOG = _ROOT / "docs/standards/requirements.json"
+_REFERENCES = _ROOT / "docs/standards/manifest.json"
+_COVERAGE = _ROOT / "docs/standards/coverage.md"
+DIMENSIONS = ("positive", "negative", "boundary", "property", "subprocess", "end-to-end")
+
+
+class RequirementValidationError(ValueError):
+ """A requirement, implementation target or evidence selector is inconsistent."""
+
+
+def _require(condition: bool, message: str) -> None:
+ if not condition:
+ raise RequirementValidationError(message)
+
+
+def _fields(value: Any, expected: set[str], label: str) -> None:
+ _require(isinstance(value, dict) and set(value) == expected, f"{label}: fields differ from schema")
+
+
+def _text(value: Any, label: str) -> str:
+ _require(isinstance(value, str) and bool(value.strip()) and "\n" not in value, f"{label}: expected nonempty text")
+ return value
+
+
+def _enum(value: Any, choices: set[str], label: str) -> str:
+ _text(value, label)
+ _require(value in choices, f"unknown {label}: {value}")
+ return value
+
+
+def _strings(value: Any, label: str, *, empty: bool = False) -> list[str]:
+ _require(isinstance(value, list) and (empty or bool(value)), f"{label}: expected nonempty list")
+ for item in value:
+ _text(item, label)
+ _require(len(value) == len(set(value)), f"{label}: duplicate values")
+ return value
+
+
+def _unique_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
+ result: dict[str, Any] = {}
+ for key, value in pairs:
+ _require(key not in result, f"duplicate JSON key: {key}")
+ result[key] = value
+ return result
+
+
+def read_catalog(path: Path) -> dict[str, Any]:
+ """Read JSON without accepting duplicate keys or invalid text."""
+ try:
+ return json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=_unique_object)
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as error:
+ raise RequirementValidationError(f"cannot read requirement catalog: {error}") from error
+
+
+def checked_references(path: Path = _REFERENCES) -> dict[str, Any]:
+ """Verify the original document catalog before using its clause inventory."""
+ spec = importlib.util.spec_from_file_location("_requirement_references", _ROOT / "scripts/check_standards.py")
+ assert spec is not None and spec.loader is not None
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ try:
+ return module.check_catalog(path)
+ except module.ReferenceValidationError as error:
+ raise RequirementValidationError(f"reference catalog: {error}") from error
+
+
+class _Collection:
+ """Record evidence after pytest selects its package target and collects tests."""
+
+ def __init__(self, output: Path) -> None:
+ self.output = output
+
+ def pytest_sessionfinish(self, session: Any, exitstatus: int) -> None:
+ if exitstatus != 0:
+ return
+ import diffable_rdf
+ from diffable_rdf.canonicalize import _FORMAT_MAP
+
+ target = session.config._diffable_rdf_package_target
+ package_root = target.origin.parent
+ owners = {}
+ for name, module in tuple(sys.modules.items()):
+ if name != "diffable_rdf" and not name.startswith("diffable_rdf."):
+ continue
+ for symbol, value in vars(module).items():
+ if not inspect.isfunction(value) or not value.__module__.startswith("diffable_rdf."):
+ continue
+ filename = inspect.getsourcefile(value)
+ if filename is not None and Path(filename).resolve().is_relative_to(package_root):
+ owners[f"{name}:{symbol}"] = (
+ "src/diffable_rdf/" + Path(filename).resolve().relative_to(package_root).as_posix()
+ )
+ exports = [name for name in diffable_rdf.__all__ if name != "__version__"]
+ _require(all(callable(getattr(diffable_rdf, name)) for name in exports), "public exports must be callable")
+ snapshot = {
+ "nodeids": sorted(item.nodeid for item in session.items),
+ "exports": sorted(exports),
+ "aliases": {name: str(value) for name, value in sorted(_FORMAT_MAP.items())},
+ "owners": owners,
+ "mode": target.mode,
+ "origin": str(target.origin),
+ }
+ self.output.write_text(json.dumps(snapshot, sort_keys=True), encoding="utf-8")
+
+
+def collect_evidence(mode: str = "source", expected_origin: Path | None = None) -> dict[str, Any]:
+ """Collect once in a neutral isolated interpreter; no test body is executed."""
+ _require(mode in {"source", "installed"}, f"unknown package mode: {mode}")
+ environment = os.environ.copy()
+ for name in ("PYTEST_ADDOPTS", "PYTEST_PLUGINS", "COVERAGE_PROCESS_START"):
+ environment.pop(name, None)
+ environment["PYTEST_DISABLE_PLUGIN_AUTOLOAD"] = "1"
+ with tempfile.TemporaryDirectory(prefix="diffable-rdf-evidence-") as directory:
+ output = Path(directory) / "collection.json"
+ command = [
+ sys.executable,
+ "-I",
+ str(Path(__file__).resolve()),
+ "--collect-output",
+ str(output),
+ "--package-under-test",
+ mode,
+ ]
+ try:
+ result = subprocess.run(
+ command,
+ cwd=directory,
+ env=environment,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
+ except (OSError, subprocess.TimeoutExpired) as error:
+ raise RequirementValidationError(f"pytest collection could not finish: {error}") from error
+ _require(result.returncode == 0, f"pytest collection failed:\n{result.stdout}\n{result.stderr}")
+ snapshot = read_catalog(output)
+ _require(snapshot["mode"] == mode, "collection package mode differs from the selected target")
+ if expected_origin is not None:
+ _require(Path(snapshot["origin"]).resolve() == expected_origin.resolve(), "collection package origin differs")
+ return snapshot
+
+
+def match_selector(selector: str, nodeids: list[str]) -> list[str]:
+ """Resolve an exact nodeid or a single function's parametrized family."""
+ _text(selector, "selector")
+ function, separator, parameters = selector.partition("[")
+ _require(
+ re.fullmatch(r"tests/(?:[A-Za-z0-9_]+/)*test_[A-Za-z0-9_]+\.py::(?:Test\w+::)?test_\w+", function) is not None
+ and (not separator or (parameters.endswith("]") and bool(parameters[:-1]))),
+ f"invalid function selector: {selector}",
+ )
+ if separator:
+ matches = [node for node in nodeids if node == selector]
+ else:
+ matches = [node for node in nodeids if node == selector or node.startswith(selector + "[")]
+ _require(not (selector in matches and len(matches) > 1), f"ambiguous function selector: {selector}")
+ _require(bool(matches), f"selector collected no tests: {selector}")
+ return matches
+
+
+def _reference(clause: Any, references: dict[str, Any], reference_root: Path) -> None:
+ _fields(clause, {"reference", "section", "anchor", "normative"}, "clause")
+ identity = _text(clause["reference"], "reference ID")
+ _require(identity in references, f"unknown reference ID: {identity}")
+ reference = references[identity]
+ section = _text(clause["section"], "section")
+ _require(type(clause["normative"]) is bool, "clause normative must be a boolean")
+ if reference["media_type"] == "text/html":
+ anchor = _text(clause["anchor"], "anchor")
+ expected = {"id": anchor, "section": section, "normative": clause["normative"]}
+ _require(expected in reference["anchors"], f"unverified clause or normative status: {identity}#{anchor}")
+ else:
+ _require(
+ clause["anchor"] is None and clause["normative"], "RFC clauses require a numbered normative text section"
+ )
+ number, _, title = section.partition(" ")
+ _require(re.fullmatch(r"\d+(?:\.\d+)*", number) is not None and bool(title), "invalid RFC section")
+ text = (reference_root / reference["path"]).read_text(encoding="utf-8")
+ pattern = rf"^{re.escape(number)}\. +{re.escape(title)}[ \t]*$"
+ _require(re.search(pattern, text, re.MULTILINE) is not None, f"missing RFC section: {identity} {section}")
+
+
+def validate_requirements(
+ catalog: Any,
+ references: dict[str, Any],
+ collection: dict[str, Any],
+ reference_root: Path,
+) -> dict[str, Any]:
+ """Validate declarative requirements against verified references and actual collection."""
+ _fields(catalog, {"schema_version", "features", "requirements"}, "requirement catalog")
+ _require(type(catalog["schema_version"]) is int and catalog["schema_version"] == 1, "unsupported schema_version")
+ nodeids = _strings(collection["nodeids"], "collected nodeids")
+ exports = _strings(collection["exports"], "collected exports")
+ features = catalog["features"]
+ _require(isinstance(features, list) and bool(features), "features must be a nonempty list")
+ feature_ids: set[str] = set()
+ actual_exports: list[str] = []
+ actual_aliases: dict[str, str] = {}
+ for feature in features:
+ _require(isinstance(feature, dict), "feature must be an object")
+ kind = _enum(feature.get("kind"), {"callable", "format"}, "feature kind")
+ fields = {"id", "kind", "description"} | ({"export"} if kind == "callable" else {"aliases", "backend"})
+ _fields(feature, fields, "feature")
+ identity = _text(feature["id"], "feature ID")
+ _require(re.fullmatch(r"[a-z][a-z0-9_.-]*", identity) is not None, f"invalid feature ID: {identity}")
+ _require(identity not in feature_ids, f"duplicate feature ID: {identity}")
+ feature_ids.add(identity)
+ _text(feature["description"], identity)
+ if kind == "callable":
+ actual_exports.append(_text(feature["export"], "export"))
+ else:
+ backend = _text(feature["backend"], "backend")
+ for alias in _strings(feature["aliases"], "aliases"):
+ _require(alias not in actual_aliases, f"duplicate format alias: {alias}")
+ actual_aliases[alias] = backend
+ _require(sorted(actual_exports) == sorted(exports), "feature inventory differs from collected public exports")
+ _require(actual_aliases == collection["aliases"], "feature inventory differs from collected format mapping")
+
+ reference_map = {item["id"]: item for item in references["references"]}
+ rows = catalog["requirements"]
+ _require(isinstance(rows, list) and bool(rows), "requirements must be a nonempty list")
+ identities: set[str] = set()
+ supported_features: set[str] = set()
+ matched: dict[str, dict[str, list[str]]] = {}
+ for row in rows:
+ _fields(
+ row,
+ {
+ "id",
+ "description",
+ "features",
+ "category",
+ "clauses",
+ "owners",
+ "status",
+ "reason",
+ "evidence",
+ "not_applicable",
+ },
+ "requirement",
+ )
+ identity = _text(row["id"], "requirement ID")
+ _require(
+ re.fullmatch(r"[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+", identity) is not None, f"invalid requirement ID: {identity}"
+ )
+ _require(identity not in identities, f"duplicate requirement ID: {identity}")
+ identities.add(identity)
+ _text(row["description"], identity)
+ row_features = set(_strings(row["features"], identity + " features"))
+ _require(row_features <= feature_ids, f"{identity}: unknown feature ID: {sorted(row_features - feature_ids)}")
+ _enum(row["category"], {"normative", "policy", "extension"}, identity + " category")
+ _require(isinstance(row["clauses"], list), f"{identity}: clauses must be a list")
+ for clause in row["clauses"]:
+ _reference(clause, reference_map, reference_root)
+ if row["category"] == "normative":
+ _require(any(clause["normative"] for clause in row["clauses"]), f"{identity}: normative support is absent")
+ _require(
+ isinstance(row["owners"], list) and bool(row["owners"]), f"{identity}: implementation owners are absent"
+ )
+ seen_owners: set[str] = set()
+ for owner in row["owners"]:
+ _fields(owner, {"role", "target", "path"}, identity + " owner")
+ _enum(owner["role"], {"local", "dependency", "boundary"}, identity + " owner role")
+ target = _text(owner["target"], "owner target")
+ _require(target not in seen_owners, f"{identity}: duplicate owner target")
+ seen_owners.add(target)
+ _require(target in collection["owners"], f"{identity}: missing implementation owner: {target}")
+ _require(collection["owners"][target] == owner["path"], f"{identity}: incorrect implementation owner path")
+ _enum(row["status"], {"supported", "out-of-profile"}, identity + " status")
+ _require(isinstance(row["evidence"], dict), f"{identity}: evidence must be an object")
+ _require(isinstance(row["not_applicable"], dict), f"{identity}: not_applicable must be an object")
+ matched[identity] = {}
+ if row["status"] == "out-of-profile":
+ _text(row["reason"], identity + " exclusion reason")
+ _require(
+ not row["evidence"] and not row["not_applicable"], f"{identity}: excluded requirement has evidence"
+ )
+ continue
+ _require(row["reason"] is None, f"{identity}: supported requirement has an exclusion reason")
+ _require(bool(row["evidence"]), f"{identity}: supported requirement has no evidence")
+ covered_dimensions = set(row["evidence"])
+ excluded_dimensions = set(row["not_applicable"])
+ _require(not covered_dimensions & excluded_dimensions, f"{identity}: contradictory evidence applicability")
+ _require(
+ covered_dimensions | excluded_dimensions == set(DIMENSIONS), f"{identity}: invalid or missing dimension"
+ )
+ for dimension, reason in row["not_applicable"].items():
+ _text(reason, identity + " " + dimension + " applicability reason")
+ for dimension, selectors in row["evidence"].items():
+ for selector in _strings(selectors, identity + " " + dimension + " selectors"):
+ matched[identity][selector] = match_selector(selector, nodeids)
+ supported_features.update(row_features)
+ _require(
+ supported_features == feature_ids,
+ f"features lack supported evidence: {sorted(feature_ids - supported_features)}",
+ )
+ return {
+ "matches": matched,
+ "collected": len(nodeids),
+ "supported": sum(row["status"] == "supported" for row in rows),
+ "excluded": sum(row["status"] == "out-of-profile" for row in rows),
+ "unique_matches": len({node for evidence in matched.values() for nodes in evidence.values() for node in nodes}),
+ "evidence_links": sum(len(nodes) for evidence in matched.values() for nodes in evidence.values()),
+ }
+
+
+def render_coverage(catalog: dict[str, Any], references: dict[str, Any], result: dict[str, Any]) -> str:
+ """Render portable coverage evidence without claiming test execution or full conformance."""
+ reference_map = {item["id"]: item for item in references["references"]}
+ lines = [
+ "# Scoped requirement evidence",
+ "",
+ "Generated from [requirements.json](requirements.json) by `python scripts/check_requirements.py --render`.",
+ "The default command checks this table without modifying it.",
+ "",
+ f"{result['supported']} supported requirements; {result['excluded']} explicit profile exclusions; "
+ f"{result['unique_matches']} distinct collected tests; {result['evidence_links']} requirement-to-test links.",
+ "",
+ "Counts describe collected evidence, not executed or passing tests. One test can support several rows.",
+ "This is not a percentage of all clauses in the copied specifications or a complete Cartesian test matrix.",
+ "Source and installed targets collect the same behavioral evidence; platform/resource and source-only",
+ "harness skips are reported by pytest when executing the suite, not hidden by collection counts.",
+ "",
+ "## Feature inventory",
+ "",
+ "| Feature | Contract | Export or format aliases |",
+ "| --- | --- | --- |",
+ ]
+ for feature in catalog["features"]:
+ names = [feature["export"]] if feature["kind"] == "callable" else feature["aliases"]
+ lines.append(
+ f"| `{feature['id']}` | {feature['description']} | " + ", ".join(f"`{name}`" for name in names) + " |"
+ )
+ lines.extend(
+ [
+ "",
+ "## Requirement map",
+ "",
+ "| Requirement | Category / status | Concrete behavior | Tests |",
+ "| --- | --- | --- | --- |",
+ ]
+ )
+ for row in catalog["requirements"]:
+ count = len({node for nodes in result["matches"][row["id"]].values() for node in nodes})
+ lines.append(
+ f"| [{row['id']}](#{row['id'].lower()}) | {row['category']} / {row['status']} | "
+ f"{row['description']} | {count} |"
+ )
+ for row in catalog["requirements"]:
+ lines.extend(
+ [
+ "",
+ f"### {row['id']}",
+ "",
+ row["description"],
+ "",
+ "Features: " + ", ".join(f"`{feature}`" for feature in row["features"]) + ".",
+ "",
+ ]
+ )
+ if row["clauses"]:
+ clauses = []
+ for clause in row["clauses"]:
+ path = reference_map[clause["reference"]]["path"]
+ fragment = "#" + clause["anchor"] if clause["anchor"] is not None else ""
+ label = f"{clause['reference']} §{clause['section']}"
+ status = "normative" if clause["normative"] else "informative context"
+ clauses.append(f"[{label}]({path}{fragment}) ({status})")
+ lines.extend(["References: " + "; ".join(clauses) + ".", ""])
+ else:
+ lines.extend(["Project contract; no normative standard algorithm is claimed.", ""])
+ for owner in row["owners"]:
+ lines.append(f"- {owner['role']}: [`{owner['target']}`](../../{owner['path']}).")
+ lines.append("")
+ if row["status"] == "out-of-profile":
+ lines.append("Outside this API profile: " + row["reason"])
+ continue
+ lines.extend(["| Dimension | Collected evidence or applicability |", "| --- | --- |"])
+ for dimension in DIMENSIONS:
+ if dimension in row["not_applicable"]:
+ evidence = "Not applicable: " + row["not_applicable"][dimension]
+ else:
+ evidence = "
".join(
+ f"`{selector}` ({len(result['matches'][row['id']][selector])})"
+ for selector in row["evidence"][dimension]
+ )
+ lines.append(f"| {dimension} | {evidence} |")
+ return "\n".join(lines).rstrip() + "\n"
+
+
+def check_coverage(path: Path, expected: str) -> None:
+ """Reject a missing or stale generated report without rewriting it."""
+ try:
+ actual = path.read_text(encoding="utf-8")
+ except (OSError, UnicodeDecodeError) as error:
+ raise RequirementValidationError(f"cannot read coverage table: {error}") from error
+ _require(actual == expected, "stale coverage table; run python scripts/check_requirements.py --render")
+
+
+def main(arguments: Sequence[str] | None = None) -> int:
+ """Check references and actual test evidence offline, optionally rendering the table."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--catalog", type=Path, default=_CATALOG)
+ parser.add_argument("--references", type=Path, default=_REFERENCES)
+ parser.add_argument("--coverage", type=Path, default=_COVERAGE)
+ parser.add_argument("--package-under-test", choices=("source", "installed"), default="source")
+ parser.add_argument("--render", action="store_true", help="explicitly update the generated coverage table")
+ parser.add_argument("--collect-output", type=Path, help=argparse.SUPPRESS)
+ args = parser.parse_args(arguments)
+ if args.collect_output is not None:
+ import pytest
+
+ return int(
+ pytest.main(
+ [
+ "--collect-only",
+ "-q",
+ "-p",
+ "no:cacheprovider",
+ "-o",
+ "addopts=",
+ "--package-under-test=" + args.package_under_test,
+ "-c",
+ str(_ROOT / "pyproject.toml"),
+ str(_ROOT / "tests"),
+ ],
+ plugins=[_Collection(args.collect_output)],
+ )
+ )
+ try:
+ references = checked_references(args.references)
+ catalog = read_catalog(args.catalog)
+ collection = collect_evidence(args.package_under_test)
+ result = validate_requirements(catalog, references, collection, args.references.parent)
+ rendered = render_coverage(catalog, references, result)
+ if args.render:
+ args.coverage.write_text(rendered, encoding="utf-8", newline="\n")
+ else:
+ check_coverage(args.coverage, rendered)
+ except (RequirementValidationError, OSError) as error:
+ print(f"requirements verification failed: {error}")
+ return 1
+ print(
+ f"verified {result['supported']} supported requirements and {result['excluded']} profile exclusions; "
+ f"{result['unique_matches']} distinct collected tests, {result['evidence_links']} requirement-to-test links "
+ f"({args.package_under_test}; collection only)"
+ )
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tests/README.md b/tests/README.md
index 99f212b..9104e8a 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ coupling independent concerns.
| Group | Responsibility | Primary dimensions |
| --- | --- | --- |
| `harness/` | Test-target selection and child-process isolation | source and wheel provenance, interpreter configuration |
-| `standards/` | Pinned reference integrity and standards evidence | document identities, source provenance, licenses, digests, clause anchors |
+| `standards/` | Pinned reference integrity and requirement evidence | provenance, licenses, digests, clauses, feature inventory, collected test selectors |
| `contracts/` | Public API, accepted graph inputs, format names, and output framing | exports, annotations, input coercion, format guarantees |
| `serialization/` | RDF document fidelity and serializer fallbacks | bases, namespaces, literals, XML, list identity, process determinism |
| `properties/` | Seeded graph invariants | losslessness, idempotence, label independence, insertion-order independence |
@@ -79,3 +79,28 @@ python scripts/check_standards.py
The same reference checks run in `standards/` under both source and installed
targets. They establish the integrity of the reference evidence, not complete
implementation of every clause of each copied specification.
+
+The [requirement map](../docs/standards/coverage.md) connects scoped behaviors to
+their pinned clauses or project policies, implementation owners and focused
+test functions. Each row identifies applicable positive, negative, boundary,
+property, subprocess and end-to-end evidence, and explains dimensions not
+separately claimed. Tests stay organized by their generic behavior groups;
+standards identifiers belong in the central catalog rather than test names.
+
+```bash
+python scripts/check_requirements.py
+python -m pytest -q --package-under-test=source tests/standards
+```
+
+This check uses one lazy full collect-only subprocess per standards test
+session. It does not execute tests recursively or expand an ordinary focused
+test selection. Actual selected-package exports, format aliases, implementation
+entry points and pytest nodeids are compared with the catalog. Removed or renamed
+evidence, incorrect normative fragments and stale generated tables fail.
+
+`python scripts/check_requirements.py --render` explicitly regenerates the
+readable table after a reviewed catalog change. The normal check is read-only
+and offline. Match counts describe collection, not pass results; full source
+and installed-wheel runs remain required, including their explicit platform
+and source-only skip reporting. See the
+[catalog schema and maintenance rules](../docs/standards/README.md#requirement-catalog-schema).
diff --git a/tests/contracts/test_prefix_map.py b/tests/contracts/test_prefix_map.py
new file mode 100644
index 0000000..8c61e9a
--- /dev/null
+++ b/tests/contracts/test_prefix_map.py
@@ -0,0 +1,30 @@
+"""Namespace-to-prefix mappings are independent snapshots of RDFLib bindings."""
+
+from rdflib import Graph
+
+from diffable_rdf import well_known_prefix_map
+
+
+def test_prefix_map_inverts_current_nonempty_bindings() -> None:
+ expected = {str(namespace): prefix for prefix, namespace in Graph().namespaces() if prefix}
+
+ mapping = well_known_prefix_map()
+
+ assert mapping == expected
+ assert all(
+ isinstance(namespace, str) and isinstance(prefix, str) and prefix for namespace, prefix in mapping.items()
+ )
+
+
+def test_prefix_map_calls_return_independent_snapshots() -> None:
+ first = well_known_prefix_map()
+ second = well_known_prefix_map()
+
+ assert first == second
+ assert first is not second
+ first.clear()
+ first["urn:caller:namespace"] = "caller"
+
+ assert second
+ assert "urn:caller:namespace" not in second
+ assert well_known_prefix_map() == second
diff --git a/tests/standards/test_requirements.py b/tests/standards/test_requirements.py
new file mode 100644
index 0000000..f63708e
--- /dev/null
+++ b/tests/standards/test_requirements.py
@@ -0,0 +1,398 @@
+"""Requirement catalogs connect scoped contracts to real collected evidence."""
+
+from __future__ import annotations
+
+import copy
+import importlib.util
+import json
+from pathlib import Path
+import subprocess
+import sys
+
+import pytest
+
+
+_ROOT = Path(__file__).resolve().parents[2]
+_STANDARDS = _ROOT / "docs/standards"
+_SELECTOR = "tests/contracts/test_example.py::test_value"
+
+
+@pytest.fixture(scope="session")
+def requirement_checker():
+ spec = importlib.util.spec_from_file_location("_test_requirement_checker", _ROOT / "scripts/check_requirements.py")
+ assert spec is not None and spec.loader is not None
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ return module
+
+
+@pytest.fixture(scope="session")
+def collected_evidence(requirement_checker, pytestconfig):
+ """One lazy child collection per session, independent of function-scoped fixtures."""
+ target = pytestconfig._diffable_rdf_package_target
+ return requirement_checker.collect_evidence(target.mode, target.origin)
+
+
+@pytest.fixture
+def catalog_case(tmp_path: Path):
+ """A complete tiny profile makes each schema mutation's purpose explicit."""
+ reference_root = tmp_path / "standards"
+ reference_root.mkdir()
+ (reference_root / "example.txt").write_text("4. Objects\n\nNames are strings.\n", encoding="utf-8")
+ references = {
+ "references": [
+ {
+ "id": "EXAMPLE",
+ "media_type": "text/html",
+ "path": "example.html",
+ "anchors": [
+ {"id": "required", "section": "1 Required", "normative": True},
+ {"id": "overview", "section": "2 Overview", "normative": False},
+ ],
+ },
+ {"id": "RFCEXAMPLE", "media_type": "text/plain", "path": "example.txt", "anchors": []},
+ ]
+ }
+ collection = {
+ "nodeids": [_SELECTOR + "[one]", _SELECTOR + "[two]", _SELECTOR + "_other"],
+ "exports": ["example"],
+ "aliases": {"ttl": "Turtle"},
+ "owners": {"diffable_rdf.example:example": "src/diffable_rdf/example.py"},
+ }
+ catalog = {
+ "schema_version": 1,
+ "features": [
+ {"id": "example", "kind": "callable", "description": "Example function", "export": "example"},
+ {"id": "format.turtle", "kind": "format", "description": "Turtle", "aliases": ["ttl"], "backend": "Turtle"},
+ ],
+ "requirements": [
+ {
+ "id": "EXAMPLE-VALUE",
+ "description": "Values retain identity.",
+ "features": ["example", "format.turtle"],
+ "category": "normative",
+ "clauses": [{"reference": "EXAMPLE", "section": "1 Required", "anchor": "required", "normative": True}],
+ "owners": [
+ {"role": "local", "target": "diffable_rdf.example:example", "path": "src/diffable_rdf/example.py"}
+ ],
+ "status": "supported",
+ "reason": None,
+ "evidence": {"positive": [_SELECTOR]},
+ "not_applicable": {
+ "negative": "The value transformation has no rejection obligation.",
+ "boundary": "Only the stated value examples are claimed.",
+ "property": "No separate randomized property is claimed.",
+ "subprocess": "No process state is involved.",
+ "end-to-end": "This row isolates one value contract.",
+ },
+ }
+ ],
+ }
+ return catalog, references, collection, reference_root
+
+
+def test_complete_catalog_matches_collected_evidence_and_current_table(requirement_checker, collected_evidence) -> None:
+ references = requirement_checker.checked_references()
+ catalog = requirement_checker.read_catalog(_STANDARDS / "requirements.json")
+ result = requirement_checker.validate_requirements(catalog, references, collected_evidence, _STANDARDS)
+
+ assert result["supported"] > 0
+ assert result["excluded"] > 0
+ assert result["unique_matches"] <= result["collected"]
+ assert result["evidence_links"] >= result["unique_matches"]
+ assert len(collected_evidence["exports"]) == 6
+ assert len(collected_evidence["aliases"]) == 15
+ expected = requirement_checker.render_coverage(catalog, references, result)
+ requirement_checker.check_coverage(_STANDARDS / "coverage.md", expected)
+
+
+def test_catalog_counts_function_families_without_similarly_named_tests(requirement_checker, catalog_case) -> None:
+ result = requirement_checker.validate_requirements(*catalog_case)
+
+ assert result["unique_matches"] == 2
+ assert result["evidence_links"] == 2
+ assert result["matches"]["EXAMPLE-VALUE"][_SELECTOR] == [_SELECTOR + "[one]", _SELECTOR + "[two]"]
+
+
+@pytest.mark.parametrize(
+ ("selector", "expected"),
+ [
+ pytest.param(_SELECTOR, [_SELECTOR + "[one]", _SELECTOR + "[two]"], id="function-family"),
+ pytest.param(_SELECTOR + "[one]", [_SELECTOR + "[one]"], id="exact-parameter"),
+ pytest.param(_SELECTOR + "_other", [_SELECTOR + "_other"], id="unparametrized-function"),
+ ],
+)
+def test_selectors_resolve_exact_functions_and_parameters(
+ requirement_checker, catalog_case, selector, expected
+) -> None:
+ assert requirement_checker.match_selector(selector, catalog_case[2]["nodeids"]) == expected
+
+
+@pytest.mark.parametrize(
+ "selector",
+ [
+ pytest.param("tests/contracts/test_example.py", id="module-only"),
+ pytest.param(_SELECTOR + "*", id="function-glob"),
+ pytest.param("test_value", id="substring"),
+ pytest.param("../" + _SELECTOR, id="parent-path"),
+ pytest.param(_SELECTOR.replace("/", "\\"), id="platform-separator"),
+ pytest.param(_SELECTOR + "[one", id="unclosed-parameter"),
+ pytest.param(_SELECTOR + "[]", id="empty-parameter"),
+ pytest.param(_SELECTOR + "[missing]", id="absent-parameter"),
+ pytest.param(_SELECTOR + "_removed", id="absent-function"),
+ ],
+)
+def test_selectors_reject_non_evidence(requirement_checker, catalog_case, selector) -> None:
+ with pytest.raises(requirement_checker.RequirementValidationError, match="selector"):
+ requirement_checker.match_selector(selector, catalog_case[2]["nodeids"])
+
+
+def test_mixed_bare_and_parametrized_function_is_ambiguous(requirement_checker) -> None:
+ with pytest.raises(requirement_checker.RequirementValidationError, match="ambiguous"):
+ requirement_checker.match_selector(_SELECTOR, [_SELECTOR, _SELECTOR + "[one]"])
+
+
+@pytest.mark.parametrize(
+ ("location", "field", "value", "message"),
+ [
+ pytest.param("catalog", "schema_version", True, "schema_version", id="boolean-version"),
+ pytest.param("catalog", "schema_version", 2, "schema_version", id="unknown-version"),
+ pytest.param("catalog", "extra", True, "fields", id="unknown-catalog-field"),
+ pytest.param("catalog", "features", [], "features", id="missing-features"),
+ pytest.param("catalog", "requirements", [], "requirements", id="missing-requirements"),
+ pytest.param("callable", "kind", "unknown", "kind", id="unknown-feature-kind"),
+ pytest.param("callable", "kind", [], "kind", id="nonstring-feature-kind"),
+ pytest.param("callable", "export", "renamed", "exports", id="stale-export"),
+ pytest.param("callable", "id", "", "feature ID", id="empty-feature-id"),
+ pytest.param("format", "aliases", ["removed"], "format mapping", id="stale-alias"),
+ pytest.param("format", "aliases", ["ttl", "ttl"], "duplicate", id="duplicate-alias"),
+ pytest.param("format", "backend", "TriG", "format mapping", id="incorrect-backend"),
+ pytest.param("row", "id", "", "requirement ID", id="empty-requirement-id"),
+ pytest.param("row", "id", "unstable name", "requirement ID", id="invalid-requirement-id"),
+ pytest.param("row", "description", "", "text", id="empty-description"),
+ pytest.param("row", "features", ["unknown"], "unknown feature", id="unknown-feature"),
+ pytest.param("row", "category", "conformant", "category", id="unknown-category"),
+ pytest.param("row", "category", {}, "category", id="nonstring-category"),
+ pytest.param("row", "status", "partial", "status", id="unknown-status"),
+ pytest.param("row", "status", [], "status", id="nonstring-status"),
+ pytest.param("row", "reason", "Excluded", "supported requirement", id="contradictory-supported-reason"),
+ pytest.param("row", "evidence", {}, "no evidence", id="absent-evidence"),
+ pytest.param("row", "owners", [], "owners", id="absent-owner"),
+ pytest.param("row", "clauses", [], "normative support", id="absent-normative-support"),
+ pytest.param("row", "not_applicable", {}, "dimension", id="missing-dimension"),
+ pytest.param("clause", "reference", "UNKNOWN", "unknown reference", id="unknown-specification"),
+ pytest.param("clause", "anchor", "absent", "unverified clause", id="unknown-anchor"),
+ pytest.param("clause", "section", "2 Wrong", "unverified clause", id="incorrect-section"),
+ pytest.param("clause", "normative", False, "normative status", id="incorrect-normative-status"),
+ pytest.param("clause", "normative", 1, "boolean", id="nonboolean-normative-status"),
+ pytest.param("owner", "target", "diffable_rdf.example:removed", "missing implementation", id="removed-owner"),
+ pytest.param("owner", "path", "src/diffable_rdf/other.py", "owner path", id="incorrect-owner-path"),
+ pytest.param("owner", "role", "parser", "role", id="unknown-owner-role"),
+ pytest.param("owner", "role", {}, "role", id="nonstring-owner-role"),
+ pytest.param("evidence", "positive", [], "nonempty list", id="empty-selector-list"),
+ pytest.param("evidence", "positive", [_SELECTOR, _SELECTOR], "duplicate", id="duplicate-selector"),
+ pytest.param("evidence", "mutation", [_SELECTOR], "dimension", id="unknown-evidence-dimension"),
+ pytest.param("evidence", "negative", [_SELECTOR], "contradictory", id="excluded-evidence-dimension"),
+ pytest.param("applicability", "boundary", "", "applicability reason", id="empty-dimension-reason"),
+ ],
+)
+def test_catalog_rejects_inconsistent_fields(
+ requirement_checker,
+ catalog_case,
+ location,
+ field,
+ value,
+ message,
+) -> None:
+ catalog, references, collection, root = catalog_case
+ row = catalog["requirements"][0]
+ objects = {
+ "catalog": catalog,
+ "callable": catalog["features"][0],
+ "format": catalog["features"][1],
+ "row": row,
+ "clause": row["clauses"][0],
+ "owner": row["owners"][0],
+ "evidence": row["evidence"],
+ "applicability": row["not_applicable"],
+ }
+ objects[location][field] = value
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match=message):
+ requirement_checker.validate_requirements(catalog, references, collection, root)
+
+
+@pytest.mark.parametrize("group", ["features", "requirements"])
+def test_catalog_rejects_duplicate_identities(requirement_checker, catalog_case, group) -> None:
+ catalog_case[0][group].append(copy.deepcopy(catalog_case[0][group][0]))
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="duplicate.*ID"):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+@pytest.mark.parametrize("field", ["id", "description", "features", "owners", "status", "evidence"])
+def test_catalog_rejects_missing_requirement_fields(requirement_checker, catalog_case, field) -> None:
+ catalog_case[0]["requirements"][0].pop(field)
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="fields"):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+def test_normative_requirement_cannot_rely_only_on_informative_context(requirement_checker, catalog_case) -> None:
+ catalog_case[0]["requirements"][0]["clauses"] = [
+ {"reference": "EXAMPLE", "section": "2 Overview", "anchor": "overview", "normative": False},
+ ]
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="normative support"):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+@pytest.mark.parametrize("section", ["4 Objects", "4 Missing", "40 Objects", "Objects"])
+def test_rfc_section_must_match_a_complete_numbered_heading(requirement_checker, catalog_case, section) -> None:
+ catalog_case[0]["requirements"][0]["clauses"] = [
+ {"reference": "RFCEXAMPLE", "section": section, "anchor": None, "normative": True},
+ ]
+
+ if section == "4 Objects":
+ assert requirement_checker.validate_requirements(*catalog_case)["supported"] == 1
+ else:
+ with pytest.raises(requirement_checker.RequirementValidationError, match="RFC section"):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+def test_out_of_profile_rows_require_reasons_and_no_evidence(requirement_checker, catalog_case) -> None:
+ excluded = copy.deepcopy(catalog_case[0]["requirements"][0])
+ excluded.update(
+ id="EXAMPLE-EXCLUDED",
+ status="out-of-profile",
+ reason="No processor API is exposed.",
+ evidence={},
+ not_applicable={},
+ )
+ catalog_case[0]["requirements"].append(excluded)
+ assert requirement_checker.validate_requirements(*catalog_case)["excluded"] == 1
+
+ excluded["evidence"] = {"positive": [_SELECTOR]}
+ with pytest.raises(requirement_checker.RequirementValidationError, match="excluded requirement has evidence"):
+ requirement_checker.validate_requirements(*catalog_case)
+ excluded["evidence"] = {}
+ excluded["reason"] = ""
+ with pytest.raises(requirement_checker.RequirementValidationError, match="exclusion reason"):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+def test_each_feature_needs_supported_evidence(requirement_checker, catalog_case) -> None:
+ catalog_case[0]["requirements"][0]["features"] = ["example"]
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="features lack supported evidence"):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+@pytest.mark.parametrize("change", ["added-export", "added-alias", "missing-owner", "duplicate-nodeid"])
+def test_actual_collection_drift_invalidates_the_catalog(requirement_checker, catalog_case, change) -> None:
+ collection = catalog_case[2]
+ if change == "added-export":
+ collection["exports"].append("new_function")
+ elif change == "added-alias":
+ collection["aliases"]["turtle"] = "Turtle"
+ elif change == "missing-owner":
+ collection["owners"].clear()
+ else:
+ collection["nodeids"].append(collection["nodeids"][0])
+
+ with pytest.raises(requirement_checker.RequirementValidationError):
+ requirement_checker.validate_requirements(*catalog_case)
+
+
+@pytest.mark.parametrize("change", ["removed", "renamed"])
+def test_removing_or_renaming_real_referenced_tests_breaks_the_gate(
+ requirement_checker,
+ collected_evidence,
+ change,
+) -> None:
+ references = requirement_checker.checked_references()
+ catalog = requirement_checker.read_catalog(_STANDARDS / "requirements.json")
+ snapshot = copy.deepcopy(collected_evidence)
+ selector = catalog["requirements"][0]["evidence"]["positive"][0]
+ matched = set(requirement_checker.match_selector(selector, snapshot["nodeids"]))
+ snapshot["nodeids"] = [node for node in snapshot["nodeids"] if node not in matched]
+ if change == "renamed":
+ snapshot["nodeids"].extend(node + "_renamed" for node in matched)
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="collected no tests"):
+ requirement_checker.validate_requirements(catalog, references, snapshot, _STANDARDS)
+
+
+def test_table_rendering_is_deterministic_and_stale_output_is_rejected(
+ requirement_checker, catalog_case, tmp_path
+) -> None:
+ catalog, references, _, _ = catalog_case
+ result = requirement_checker.validate_requirements(*catalog_case)
+ rendered = requirement_checker.render_coverage(catalog, references, result)
+ assert requirement_checker.render_coverage(catalog, references, result) == rendered
+ assert "2 distinct collected tests" in rendered
+ assert "not executed or passing tests" in rendered
+
+ table = tmp_path / "coverage.md"
+ table.write_text(rendered, encoding="utf-8")
+ requirement_checker.check_coverage(table, rendered)
+ table.write_text(rendered + "extra\n", encoding="utf-8")
+ with pytest.raises(requirement_checker.RequirementValidationError, match="stale coverage table"):
+ requirement_checker.check_coverage(table, rendered)
+ assert table.read_text(encoding="utf-8") == rendered + "extra\n"
+
+
+@pytest.mark.parametrize("content", ['{"schema_version":1,"schema_version":2}', "{", "[]"])
+def test_cli_reports_invalid_catalogs(
+ requirement_checker, tmp_path, monkeypatch, capsys, content
+) -> None:
+ catalog = tmp_path / "requirements.json"
+ catalog.write_text(content, encoding="utf-8")
+ monkeypatch.setattr(requirement_checker, "collect_evidence", lambda mode: {})
+
+ assert requirement_checker.main(["--catalog", str(catalog)]) == 1
+ assert "requirements verification failed" in capsys.readouterr().out
+
+
+def test_reference_integrity_is_checked_before_requirements(requirement_checker, tmp_path) -> None:
+ manifest = tmp_path / "manifest.json"
+ manifest.write_text("{}", encoding="utf-8")
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="reference catalog"):
+ requirement_checker.checked_references(manifest)
+
+
+def test_collector_sanitizes_pytest_settings_and_verifies_origin(requirement_checker, tmp_path, monkeypatch) -> None:
+ expected = tmp_path / "package/__init__.py"
+ monkeypatch.setenv("PYTEST_ADDOPTS", "--collect-only --ignore=tests")
+ monkeypatch.setenv("PYTEST_PLUGINS", "unavailable_plugin")
+ monkeypatch.setenv("PYTHONPATH", "/unrelated/source")
+ calls = []
+
+ def collect(command, **options):
+ calls.append((command, options))
+ assert command[:2] == [sys.executable, "-I"]
+ assert "PYTEST_ADDOPTS" not in options["env"]
+ assert "PYTEST_PLUGINS" not in options["env"]
+ assert options["env"]["PYTEST_DISABLE_PLUGIN_AUTOLOAD"] == "1"
+ assert not Path(options["cwd"]).is_relative_to(_ROOT)
+ output = Path(command[command.index("--collect-output") + 1])
+ output.write_text(json.dumps({"mode": "installed", "origin": str(expected)}), encoding="utf-8")
+ return subprocess.CompletedProcess(command, 0, "", "")
+
+ monkeypatch.setattr(requirement_checker.subprocess, "run", collect)
+ assert requirement_checker.collect_evidence("installed", expected)["origin"] == str(expected)
+ assert len(calls) == 1
+ with pytest.raises(requirement_checker.RequirementValidationError, match="origin differs"):
+ requirement_checker.collect_evidence("installed", tmp_path / "other/__init__.py")
+
+
+def test_collector_propagates_collection_failure(requirement_checker, monkeypatch) -> None:
+ monkeypatch.setattr(
+ requirement_checker.subprocess,
+ "run",
+ lambda *args, **kwargs: subprocess.CompletedProcess(args[0], 2, "collection failed", "invalid package target"),
+ )
+
+ with pytest.raises(requirement_checker.RequirementValidationError, match="pytest collection failed"):
+ requirement_checker.collect_evidence()