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()