Skip to content

docs: state a position on format assertion, and give the spec validation semantics - #47

Merged
fas89 merged 1 commit into
open-data-protocol:mainfrom
fas89:docs/format-annotation-only
Sep 13, 2026
Merged

fas89 merged 1 commit into
open-data-protocol:mainfrom
fas89:docs/format-annotation-only

Conversation

@fas89

@fas89 fas89 commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

The conformance corpus surfaced this and recorded it as open: is format assertive in FLUID? The specification did not say, so the corpus took the weaker reading and put the assertive cases in optional/. Right call for a corpus — but it pushed the decision onto every implementer.

The position

format is an annotation, never an assertion. A validator MUST NOT reject a FLUID document solely because a string fails to match the format named for it; it MAY surface the mismatch as a warning.

Stated plainly rather than left to be discovered: a document that validates is not thereby guaranteed to carry a well-formed metadata.owner.email. Exactly three format keywords appear across all twelve published schemas — uri (54), email (17), date-time (10) — and all three are descriptive.

Why, beyond the 2020-12 default

Conformance must not depend on which validator you run. format vocabularies are optional in Draft 2020-12 and implementations differ in which formats they recognise and what they pull in to check them. Assertive format would make the same document conformant in one language and non-conformant in another — which defeats the purpose of publishing a corpus at all.

The choice is not symmetric. Declaring format assertive would invalidate documents that are valid today. That is a narrowing, which GOVERNANCE.md forbids between versions ("minor versions may add. They may not narrow") and scripts/check-compat.py enforces on every PR. Annotation-only is the only reading still available to a pre-1.0 spec 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.

A gap this exposed

The spec had no validation-semantics section at all — it never said how a document is validated, which schema applies, or which draft governs keyword meaning. format merely happened to expose that. The new section covers all of it.

It also calls out a trap: FLUID has its own field named format — exposes[].binding.format (bigquery_table, snowflake_table, …) and the format keys in the acquisition blocks. Those are constrained by enum, and enum is assertive. The names collide and the annotation rule does not apply to them.

Worth recording: I initially believed bigquery_table was a non-standard format keyword in the schema and nearly shipped that as an argument. It is not — it appears only inside examples blocks, where it is FLUID's own field. A schema walker that does not know examples holds data misreads it. The claim was dropped and replaced with the two above, which are checkable.

Also corrected

The page claimed 0.7.4 was the latest published schema. It is 0.7.5.

tests/README.md

The question is kept, not deleted — marked answered and pointing at the spec. A question the corpus raised and the specification then settled is the outcome that section exists to produce; deleting the trail would hide that it was ever in doubt. The section is renamed from "Known ambiguities" to "Questions the corpus has surfaced", since one is now settled.

The second question — the reference implementation validating 2020-12 schemas with a Draft7Validator — stays open, and now has a heading of its own instead of being silently swallowed by the first.

Verification

No corpus data changed; the diff is two docs and one docstring.

Gate Result
conformance/run.py 480/480, 0 failed, 0 unexpectedly passed
conformance/run.py --no-optional 478/478
tests/meta_test.py (as CI runs it) 19/19
check-compat.py pairwise 0.7.1→0.7.5 all 4 exit 0
specs/ regeneration drift 0 files (no schema/ change)

🤖 Generated with Claude Code

…ion semantics

The conformance corpus surfaced the question and recorded it as open: is
`format` assertive in FLUID? The specification did not say, so the corpus took
the weaker of the two readings and put the assertive cases in `optional/`. That
was the right call for a corpus, but leaving it open pushed the decision onto
every implementer.

FLUID now states the answer. `format` is an annotation: a validator MUST NOT
reject a document solely because a string fails to match the format named for
it, and MAY surface the mismatch as a warning. The consequence is stated
plainly rather than left to be discovered — a document that validates is not
thereby guaranteed to carry a well-formed `metadata.owner.email`.

Two reasons beyond the Draft 2020-12 default, both specific to this project:

Conformance must not depend on which validator you run. `format` vocabularies
are optional in 2020-12 and implementations differ in which formats they
recognise. Assertive `format` would make the same document conformant in one
language and not in another, which defeats the purpose of publishing a corpus.

The choice is not symmetric. Declaring `format` assertive would invalidate
documents valid today — a narrowing, which GOVERNANCE.md forbids between
versions and `scripts/check-compat.py` enforces on every pull request.
Annotation-only is the only reading still available to a pre-1.0 spec that has
published twelve schema versions. A future version may add assertive checking
behind a new opt-in keyword; it may not retroactively sharpen this one.

The section is also the first place the spec says how a document is validated
at all, which was a gap `format` merely happened to expose.

It calls out one trap explicitly: FLUID has its own FIELD named `format` —
`exposes[].binding.format`, and the `format` keys in the acquisition blocks —
constrained by `enum`, and `enum` IS assertive. The names collide and the
sentence about annotations does not apply to them.

Also corrected: the page claimed 0.7.5 was unpublished, naming 0.7.4 as latest.

tests/README.md keeps the question rather than deleting it, now marked answered
and pointing at the spec — a question the corpus raised and the specification
then settled is the outcome that section exists to produce, and deleting the
trail would hide that it was ever in doubt. The second question, the Draft 7
validator on 2020-12 schemas, stays open and now has a heading of its own
instead of being swallowed by the first.

No corpus data changed. Corpus 480/480 (478 without optional), meta-test 19/19,
pairwise backward compatibility 0.7.1 through 0.7.5 all clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fas89
fas89 merged commit 510b065 into open-data-protocol:main Sep 13, 2026
3 checks passed
fas89 added a commit to fas89/fluid that referenced this pull request Sep 13, 2026
…g paths

A path-filtered check cannot be a REQUIRED check. On a pull request that does
not match the filter the context never reports at all, so branch protection
waits for a report that will never arrive and the pull request hangs
permanently, with nothing to click.

That is why main still requires no status checks: turning the existing three on
as they were would have bricked the next docs PR rather than protected
anything.

Not hypothetical here. PR open-data-protocol#47 was a docs change and received these three checks
only because it happened to also touch conformance/run.py. PR open-data-protocol#48 — LICENSE
and CONTRIBUTING.md — reported no checks at all and merged with nothing having
run.

The push trigger keeps its filter: nothing waits on a push, so skipping the
corpus for a README typo on main costs nothing and saves a runner. On pull
requests the corpus is about forty seconds, which is cheaper than one person
wondering why their pull request will not merge.

Two properties this file already had, now load-bearing and worth stating:

It declares no `schedule:`. The two workflows GitHub auto-disabled for
inactivity — link-check and schema-sync — are exactly the two that carry one,
and this repository went 73 days between commits, past the 60-day threshold. A
required context living in a scheduled workflow would be switched off by a
quiet spell and hang every pull request thereafter.

`corpus` and `backward compatibility` both declare `needs: meta`. A job skipped
because its `needs` failed still reports a check run, and branch protection
counts skipped as satisfied. So the three must be required TOGETHER: requiring
only the latter two would let a pull request merge while `gates can fail` is
red, and that job's entire purpose is proving the other two can go red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant