diff --git a/conformance/run.py b/conformance/run.py index 8dca128..2fd7cdf 100644 --- a/conformance/run.py +++ b/conformance/run.py @@ -163,15 +163,21 @@ def _build_validators(schema: Dict[str, Any]) -> Dict[bool, Any]: In JSON Schema 2020-12 `format` is an ANNOTATION by default, not an assertion: a conformant validator may accept "not-an-email" for - {"format": "email"}. FLUID has never said which it wants, and its own - reference implementation does not assert formats. - - So the core corpus is checked WITHOUT format assertion -- the weaker, - universally-agreed reading -- and cases that depend on formats being - assertive live in tests/optional/, exactly where JSON-Schema-Test-Suite - puts its own format tests. That way the core corpus stays runnable by any - conformant validator, and format behaviour is asserted where an - implementation opts into it, rather than being silently assumed. + {"format": "email"}. FLUID now says the same -- a validator MUST NOT reject + a document solely because a string fails the format named for it -- under + "Validation semantics" in the specification. Its reference implementation + does not assert formats either. + + So the core corpus is checked WITHOUT format assertion, which is both the + specified reading and the universally-agreed one, and cases that depend on + formats being assertive live in tests/optional/, exactly where + JSON-Schema-Test-Suite puts its own format tests. That keeps the core + corpus runnable by any conformant validator while still serving the + implementations that opt into asserting formats. + + This split predates the specification stating a position and is unchanged + by it: what changed is that the core tier's behaviour is now required + rather than merely the safer of two available readings. """ try: from jsonschema import Draft202012Validator, FormatChecker diff --git a/docs/schema/specification.md b/docs/schema/specification.md index 3568385..85d62c4 100644 --- a/docs/schema/specification.md +++ b/docs/schema/specification.md @@ -1,7 +1,7 @@ # Fluid Protocol Specification ::: tip Latest schema version -The latest published JSON Schema is **0.7.4**. This page is the narrative protocol specification; for the version-specific, field-by-field reference see the [**Cheatsheet**](/fluid/schema/cheatsheet), the [**Anatomy**](/fluid/schema/anatomy), and the [**Versions**](/fluid/schema/versions) index (raw JSON Schema + generated HTML per version). +The latest published JSON Schema is **0.7.5**. This page is the narrative protocol specification; for the version-specific, field-by-field reference see the [**Cheatsheet**](/fluid/schema/cheatsheet), the [**Anatomy**](/fluid/schema/anatomy), and the [**Versions**](/fluid/schema/versions) index (raw JSON Schema + generated HTML per version). ::: This document provides the complete, official specification for the FLUID (Federated Layered Unified Interchange Definition) protocol. It is intended for data architects, platform engineers, and developers who are building the next generation of data infrastructure, as well as for vendors seeking to make their tools compliant with this open standard. @@ -84,6 +84,32 @@ This document provides the complete, official specification for the FLUID (Feder --- +## Validation semantics + +A FLUID document is validated against the published JSON Schema whose version matches its `fluidVersion`. Every published schema declares `"$schema": "https://json-schema.org/draft/2020-12/schema"`, so JSON Schema Draft 2020-12 defines the meaning of every keyword except where this section says otherwise. + +### `format` is an annotation, never an assertion + +A validator **MUST NOT** reject a FLUID document solely because a string does not match the `format` named for it. A validator **MAY** surface the mismatch as a warning, and a governance or linting layer built on FLUID **MAY** treat it as an error of its own; neither affects whether the document is a valid FLUID document. + +The consequence is worth stating plainly rather than leaving for a reader to discover: **a document that validates is not thereby guaranteed to carry a well-formed `metadata.owner.email`.** Three formats appear across the published schemas — `uri`, `date-time` and `email` — and all three are descriptive. + +This is the Draft 2020-12 default, and FLUID keeps it for two reasons of its own. + +The first is that conformance must not depend on which validator you run. `format` vocabularies are optional in Draft 2020-12 and implementations differ widely in which formats they recognise and what they pull in to check them. Were FLUID to make `format` assertive, the same document could be conformant in one language and non-conformant in another, which would defeat the purpose of publishing a conformance corpus at all. + +The second is that the choice is not symmetric. Declaring `format` assertive would invalidate documents that are valid today — a narrowing, which [GOVERNANCE.md](https://github.com/open-data-protocol/fluid/blob/main/GOVERNANCE.md) forbids between versions and `scripts/check-compat.py` enforces on every pull request. Annotation-only is therefore the only reading available to a pre-1.0 specification that has already published twelve schema versions. A future version may add assertive checking behind a new, opt-in keyword; it may not retroactively sharpen this one. + +Implementations that do want to assert formats are served by the conformance corpus rather than left to guess: the cases that depend on assertion live in [`tests/optional/`](https://github.com/open-data-protocol/fluid/tree/main/tests/optional), separated from the core tier for exactly this reason, and `conformance/run.py` runs them under a format-asserting validator. + +### FLUID's own `format` field is unrelated + +FLUID defines a **field** named `format` in several places — `exposes[].binding.format` (`bigquery_table`, `snowflake_table`, `gcs_file`, …) and the `format` keys in the acquisition blocks. These are ordinary FLUID fields constrained by `enum`, and `enum` **is** assertive: a `binding.format` outside the enumerated set makes the document invalid. + +The collision of names is unfortunate and is called out here because it is easy to read "`format` is an annotation" as applying to them. It does not. The sentence above is about the JSON Schema *keyword*; this paragraph is about a FLUID *field* that happens to share its spelling. + +--- + ## 1. Specification Root The FLUID definition is a YAML or JSON file (`.fluid.yml` OR `.fluid.json`) with the following root-level objects. diff --git a/tests/README.md b/tests/README.md index 9ad9861..380ec61 100644 --- a/tests/README.md +++ b/tests/README.md @@ -30,7 +30,7 @@ The core/optional split follows [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite). The core tier asserts only what every conformant JSON Schema 2020-12 validator must do. The optional tier asserts behaviour the specification permits but does -not require — today, `format` assertion (see "Known ambiguities" below). +not require — today, `format` assertion (see "Format assertion" below). ## File format @@ -124,22 +124,40 @@ The file is how a strict gate stays adoptable on the day it lands: a real shortcoming is recorded in the repository instead of being hidden by weakening the corpus. -## Known ambiguities the corpus has surfaced +## Questions the corpus has surfaced Recorded here because a conformance suite's job is to turn disagreements into -written questions rather than silent divergence. - -**Is `format` assertive in FLUID?** The specification does not say. In JSON -Schema 2020-12, `format` is an annotation by default — a conformant validator -may accept `"not-an-email"` for `{"format": "email"}`. The reference -implementation does not assert formats. Until FLUID states a position, the core -tier does not assert them either, and the cases that depend on assertion live -in `optional/`. A FLUID document is therefore **not** guaranteed to have a -well-formed `metadata.owner.email` merely because it validates. - -**The reference implementation validates a 2020-12 schema with a Draft 7 -validator.** `fluid_build/schema_manager.py` constructs a -`jsonschema.Draft7Validator` while every published schema declares +written questions rather than silent divergence. One is now answered by the +specification; the other is still open. + +### Format assertion + +*Is `format` assertive in FLUID?* + +**Answered — it is not.** The specification now states that `format` is an +annotation, and that a validator MUST NOT reject a document solely because a +string fails to match the format named for it, under [Validation +semantics](https://open-data-protocol.github.io/fluid/schema/specification#validation-semantics). + +This corpus had already assumed that reading, because it is the Draft 2020-12 +default and the weaker of the two; the difference is that the core tier now +runs without format assertion because the specification says so, rather than +because the question was open. The cases that depend on assertion stay in +`optional/` for implementations that opt in. A FLUID document is still **not** +guaranteed to have a well-formed `metadata.owner.email` merely because it +validates — that is now a stated property rather than an accident. + +Recorded here rather than deleted: a question this corpus surfaced and the +specification then settled is the outcome the section exists to produce, and +removing the trail would hide that the answer was ever in doubt. + +### Draft 7 validator on a 2020-12 schema + +*Does the reference implementation validate 2020-12 schemas correctly?* + +**Still open.** It validates a 2020-12 schema with a Draft 7 validator: +`fluid_build/schema_manager.py` constructs a `jsonschema.Draft7Validator` +while every published schema declares `"$schema": "https://json-schema.org/draft/2020-12/schema"`. Today this is latent rather than active: the schemas use only `$defs` from the 2020-12-only keyword set, and `#/$defs/...` references resolve under Draft 7 as ordinary