Skip to content

[contracts] Publish JSON Schemas + CI drift check (closes #196) #225

Description

@dgenio

Problem statement

Open issue #196 specifies JSON Schemas for contextweaver's public on-the-wire types: catalog YAML/JSON files, ChoiceCard, ResultEnvelope, RouteTrace, BuildStats, GraphManifest. All have to_dict() / from_dict() and are documented in code — but no schemas are published.

Schemas unlock:

The schemas exist implicitly in to_dict/from_dict and the dataclasses; codifying them is mechanical.

Proposed solution

  1. jsonschema_gen helper in serde.py — small Python function that introspects a dataclass and emits a JSON Schema. Or, if cleaner, a separate src/contextweaver/_schema_gen.py (≤300 lines).
  2. schemas/*.schema.json — six new committed schemas:
    • catalog.schema.json (for SelectableItem[])
    • choice_card.schema.json
    • result_envelope.schema.json
    • route_trace.schema.json
    • build_stats.schema.json
    • graph_manifest.schema.json
  3. Stable $id URLs — host under the mkdocs site at https://dgenio.github.io/contextweaver/schemas/v0/<name>.schema.json.
  4. make schemas — regenerates schemas from dataclasses.
  5. make schemas-check — fails on drift; wired into make ci.
  6. docs/contracts.md — describes the schemas, links each to its dataclass, and explains versioning (/v0/, /v1/, …).
  7. # yaml-language-server: $schema=... header on examples/sample_catalog.yaml (and any other committed YAML catalogs) so VS Code picks the schema up automatically.

Alternatives considered

  • Hand-write schemas. Rejected — drift inevitable; generator + drift check is the right discipline.
  • Use pydantic.TypeAdapter for generation. Rejected — Pydantic isn't a core dep; introducing it for schema generation alone is overkill.
  • Use dataclasses-jsonschema (external). Rejected — small custom generator is more controllable and matches the repo's stdlib-only preference.
  • Skip the make schemas-check step. Rejected — without it, drift ships silently; defeats the purpose.

Affected modules

src/contextweaver/_schema_gen.py (new) or extend serde.py, schemas/*.schema.json (6 new), Makefile, docs/contracts.md (new), mkdocs.yml, examples/sample_catalog.yaml, .github/workflows/ci.yml.

Estimated effort

M (3–5 days)

Baseline

No JSON Schemas published. VS Code YAML autocomplete doesn't work on catalog files. No cross-language contract. Drift between dataclass and prose documentation in docs/gateway_spec.md is checked manually only.

Success metric

Scope

Schema generator + 6 schemas + 1 docs page + make targets + CI step + YAML header on the sample catalog.

Non-goals

  • Not adding cross-language ports (out of scope; the schemas enable them).
  • Not auto-publishing schemas to a third-party registry (schemastore.org); host on mkdocs only for now.
  • Not adding pydantic as a dep.
  • Not changing any dataclass fields to fit a schema (the schemas conform to the dataclasses).

Design constraints

  • Generator stdlib-only.
  • Deterministic generation: same dataclasses → byte-identical schemas.
  • Schema content must match the prose in docs/gateway_spec.md for ChoiceCard size bounds; where prose is ambiguous, take it as authoritative and surface the ambiguity rather than guessing.
  • Sub-300-line module guideline.

Depends on

None.

Suggested order

Last in M2 (closes #196).

Acceptance Criteria

  • schemas/catalog.schema.json exists.
  • schemas/choice_card.schema.json exists (covers the size bounds from docs/gateway_spec.md as maxLength / maxProperties).
  • schemas/result_envelope.schema.json exists.
  • schemas/route_trace.schema.json exists.
  • schemas/build_stats.schema.json exists.
  • schemas/graph_manifest.schema.json exists.
  • All schemas registered with a stable $id (https://dgenio.github.io/contextweaver/schemas/v0/<name>.schema.json).
  • make schemas regenerates all 6 schemas from dataclasses deterministically.
  • make schemas-check exits non-zero on drift; wired into make ci.
  • docs/contracts.md documents the schemas, links each to its dataclass, and explains the versioning policy.
  • examples/sample_catalog.yaml gains a top-of-file # yaml-language-server: $schema=... header.
  • Round-trip CI: fixture from to_dict() validates against the schema.
  • make ci passes.

Validation

pip install -e ".[dev]"
make schemas
git diff --quiet schemas/  # passes on clean tree
# Mutate ChoiceCard field name (e.g. in a fixture branch):
# (edit src/contextweaver/envelope.py)
make schemas-check  # exits non-zero
make schemas        # regenerate
git diff schemas/   # shows the rename
make ci

Manual: open examples/sample_catalog.yaml in VS Code with the Red Hat YAML extension; confirm tab-completion on a SelectableItem field works.

Files likely touched

src/contextweaver/_schema_gen.py (new) or extension of serde.py, schemas/*.schema.json (6 new), Makefile, docs/contracts.md (new), mkdocs.yml, examples/sample_catalog.yaml, .github/workflows/ci.yml.

Risk / rollback

Low — schemas + generator are additive. CI step is gated; can be soft if needed. Revert by removing the schema files, the generator, and the CI step.

Security/privacy notes

None. Schemas contain field names and types only.

Agent-ready notes

  • Starting points: src/contextweaver/serde.py for the existing serialization layer. src/contextweaver/envelope.py for the dataclasses needing schemas. JSON Schema Draft 2020-12 spec. docs/gateway_spec.md for ChoiceCard size bounds.
  • Constraints: Generator stdlib-only. Schemas must match to_dict() output exactly. ChoiceCard size bounds must reflect the gateway spec prose.
  • Review focus for Copilot code review:
    • Generator handles all dataclass field types (str, int, float, bool, list, dict, Optional, enums, nested dataclasses).
    • Schemas validate against fixtures produced by to_dict().
    • $id URLs are stable + versioned under /v0/.
    • Drift check actually fails when a field renames.

Cross-references

References

Size

M

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/adaptersProtocol adapters: MCP, A2Aarea/routingRouting engine: catalog, graph, router, cardscomplexity/averageStandard effort, moderate familiarity neededcontractsenhancementNew feature or requestpriority/mediumMedium priority — production readiness

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions