Skip to content

spec: general referential-integrity gate — every $ref must resolve - #277

Merged
mdheller merged 1 commit into
mainfrom
feat/schema-referential-integrity-gate
Aug 4, 2026
Merged

spec: general referential-integrity gate — every $ref must resolve#277
mdheller merged 1 commit into
mainfrom
feat/schema-referential-integrity-gate

Conversation

@mdheller

@mdheller mdheller commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Closes the pending register item: make spec pointer-resolution a general validator that excludes itself.

The gap

334 schemas, ~135 $ref links — but the per-example validators only exercise schemas that ship examples. A schema declaring a $ref to a moved / renamed / mistyped target sails straight through: nothing loads it, so nothing notices the reference resolves to a hole. That is the declared-not-enforced defect at the spec's own reference layer.

The gate

tools/validate_schema_references.py asserts, for every schemas/*.json:

  1. valid JSON and a structurally valid JSON Schema (Draft 2020-12 metaschema check_schema);
  2. every $ref — internal #/…, relative File.json (optionally #/fragment), or canonical $id URI — resolves to a real schema and a real pointer target inside it.

Self-excluding + teeth

  • Excludes itself: scan targets schemas/ only; the validator lives in tools/, never in the scanned set.
  • Proves its own teeth every run: an inline synthetic negative control (dangling file ref, dangling internal pointer, file+fragment miss, malformed schema, + valid counterparts), built in memory, never touching schemas/. If it fails to trip, the gate exits 2 and certifies nothing.
  • Fails closed on an empty scan.

Result

OK: 334 schemas structurally valid; all 135 $refs resolve to a real target

Wired into make validate (CI runs it via validate-ops-history.yml), so the spec enforces its own referential integrity going forward — same shape as the moving-tag ratchet: clean today, kept clean by a gate that can actually fail.

334 schemas, 135 $refs, but the per-example validators only exercise schemas
that ship examples. A schema declaring a $ref to a moved/renamed/typo'd target
sails through: nothing loads it, so nothing notices the reference points at a
hole. That is declared-not-enforced at the spec's own reference layer.

validate_schema_references.py asserts, for every schemas/*.json:
  1. valid JSON + structurally valid JSON Schema (Draft 2020-12 metaschema)
  2. every $ref (internal #/…, relative File.json[#/…], or canonical $id URI)
     resolves to a real schema AND a real pointer target inside it

Self-excluding (a self-validating checker must exclude itself): scans schemas/
only; the validator lives in tools/, never in the scanned set; its teeth are
proven every run by an inline SYNTHETIC negative control (dangling file ref,
dangling internal pointer, file+fragment, malformed schema) that never touches
schemas/. If that control fails to trip, the gate exits 2 and certifies nothing.
Also fails closed on an empty scan (won't green a scan of zero schemas).

Wired into `make validate` (which CI runs), so the whole spec now enforces its
own referential integrity going forward. First run: 334 schemas clean, all 135
$refs resolve.
@mdheller
mdheller merged commit 3d766d3 into main Aug 4, 2026
9 checks passed
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