Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions conformance/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 27 additions & 1 deletion docs/schema/specification.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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.
Expand Down
48 changes: 33 additions & 15 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
Loading