From 4c5f9d3ba5fca1c47b9cff851295373950f92e91 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:03:34 -0400 Subject: [PATCH 1/2] chore(validate): add duplicate $id guardrail script --- scripts/check_duplicate_schema_ids.py | 80 +++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 scripts/check_duplicate_schema_ids.py diff --git a/scripts/check_duplicate_schema_ids.py b/scripts/check_duplicate_schema_ids.py new file mode 100644 index 0000000..5a09af6 --- /dev/null +++ b/scripts/check_duplicate_schema_ids.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Fail if JSON Schemas define duplicate $id values. + +This guardrail prevents the spec from drifting into multi-authority schema identity. + +We scan: +- schemas/*.json +- schemas/control-plane/*.json +- schemas/control-plane/*.schema.json + +Rules: +- Each `$id` must be unique across the repository. +- If a schema lacks `$id`, it is ignored (some sub-schemas may omit it). + +Exit: +- 0 if no duplicates found +- 1 if duplicates exist +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + + +def load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def main() -> int: + repo = Path(__file__).resolve().parents[1] + + globs = [ + repo / "schemas", + repo / "schemas" / "control-plane", + ] + + paths: list[Path] = [] + for root in globs: + if not root.exists(): + continue + for p in root.glob("*.json"): + paths.append(p) + for p in root.glob("*.schema.json"): + paths.append(p) + + seen: dict[str, Path] = {} + dups: list[tuple[str, Path, Path]] = [] + + for p in sorted(set(paths)): + try: + obj = load_json(p) + except Exception as e: + print(f"WARN: could not parse {p}: {e}", file=sys.stderr) + continue + + sid = obj.get("$id") + if not sid: + continue + + if sid in seen: + dups.append((sid, seen[sid], p)) + else: + seen[sid] = p + + if dups: + print("ERROR: duplicate schema $id values detected:", file=sys.stderr) + for sid, a, b in dups: + rel_a = a.relative_to(repo) + rel_b = b.relative_to(repo) + print(f"- {sid}\n - {rel_a}\n - {rel_b}", file=sys.stderr) + return 1 + + print(f"OK: {len(seen)} unique schema $id values") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 78506cd2eb0fe3940567a839f39b8f82e15b36ed Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:11:53 -0400 Subject: [PATCH 2/2] ci(validate): add duplicate schema $id guardrail --- .github/workflows/validate.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 8b7383c..7713bf7 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -42,6 +42,10 @@ jobs: done exit $failed + - name: Guardrail: detect duplicate schema $id values + run: | + python3 scripts/check_duplicate_schema_ids.py + - name: Validate examples against schemas run: | python3 - << 'EOF'