Skip to content
Open
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
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,60 @@ this is a crate-version release only.
- Recommended relation vocabulary `frame::rel` (#7).
- Embedding fingerprint format and exact-match rule (E1) (#11).

### Removed
- **Breaking:** `Capabilities.upsert`, `Capabilities.subscribe`, and
`QueryCapability.filters` — negotiable at handshake but unreachable by any
host. Wire-compatible; Rust API breaking (#5, #6, #11).

### Fixed
- **`SPEC.md` §9's `verify` example no longer fails the schema `SPEC.md` ships.**
Both envelopes carried `"id": "v1"`, but §3.2 grants an `id` only to
`query`/`frames`/`error`, the reference `Envelope::Verify`/`Verified` have no
such field, and the schema is `additionalProperties: false` — the example was
invalid against the protocol's own definition. The `id`s are removed;
`verify` correlates by full frame identity, not by envelope id. Root cause:
`schema/validate-examples.py` checked `examples/` but never `SPEC.md`, so the
one example surface with no machine check was the one that drifted. It now
validates every fenced `jsonc` block in `SPEC.md` too (comments and documented
placeholders normalized away, structure checked), and CI's existing `schema`
job therefore catches this class of drift.
- **Regression guard for the `ContextQuery` `required` fix.** The schema change
itself landed independently in #63; this adds the test that keeps it fixed —
an ordinary unfiltered, unanchored query must satisfy the schema's *own*
`required` array (read from the schema, so it cannot drift into a stale
snapshot). A cross-audit of all 16 shared types confirms no other type demands
a field its serializer elides — this bug class has now recurred twice
(`ContextFrame` in PR #44, `ContextQuery` in #63), so it is worth a standing
check rather than another one-off fix.
- **§G2 and §D1 are now actually verified, not merely asserted.** Both named
`frame-validity` as their verifier while neither `target_uri` nor the frame's
own `content_digest` was read by any check — the self-attestation §11.1
exists to rule out. `check_frames` now rejects a relation with an empty
`target_uri` (§G2) and a present-but-malformed `content_digest` (§D1); its
evidence string had claimed "well-formed digests" while accepting
`sha256:abc`. §D1 was found by auditing the other ten rules that cite
`frame-validity` after §G2 turned out to be unenforced; the remaining nine
were confirmed enforced.
- **`contextgraph-host::wire` docs no longer invert a MUST NOT.** The module
said concurrency is "negotiated by observation, not by a capability flag",
contradicting `SPEC.md` §3.2 and the shipped `Capabilities::correlation`: a
host **MUST NOT** send an `id` to a provider that did not declare correlation.
- JSON Schema: a `ContextFrame`'s `required` is now exactly what the reference
serializer always emits (`id`, `kind`, `title`, `score`, `token_cost`).
`provenance` and `relations` were listed as globally required but are
`skip_serializing_if = Vec::is_empty` in the reference type and required by no
frame-validity check, so a Rust-serialized frame with no edges failed schema
validation. Surfaced by ADR 0006's wire-conformance test — the first to
validate serialized frames (not just hand-authored examples) against the
schema. `content` remains governed per-representation by the existing `allOf`.

### Changed
- **Breaking:** `token_cost` MUST now equal the canonical count for its content.
Providers that under-declared cost were previously green (#8).
- Withdrew the incorrect claim that CGP rides JSON-RPC 2.0 (#4).
- Code comments cite `SPEC.md` anchors instead of a private repository (#3).

### Added
- [`schema/contextgraph-envelope.schema.json`](./schema/contextgraph-envelope.schema.json) — a
machine-readable JSON Schema (Draft 2020-12) for the Context Graph Protocol envelope and all wire
types. Validates in any language (`ajv`, Python `jsonschema`, Rust
Expand Down
11 changes: 4 additions & 7 deletions schema/validate-examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

Exits 0 if every message in examples/, every reference-serialized vector, and
every fenced example in SPEC.md is valid under schema/ — and the schema's `$id`
is the URL that actually serves it. Exits 1 otherwise.
resolves to a byte-identical served copy. Exits 1 otherwise.
No third-party dependencies beyond `jsonschema` (pip install jsonschema).

The three example surfaces are deliberately different in kind:
The four example surfaces are deliberately different in kind:

* `examples/` is hand-authored — it proves the schema accepts what a human
writes, and is what a provider author diffs against.
Expand All @@ -23,11 +23,8 @@
that the reference envelope has no field for, and that the schema's
`additionalProperties: false` rejects. The spec's own examples are now held
to the spec's own schema.

`$id` is checked separately (5, below): it is the schema's public identity, and
a schema whose identity URL 404s is quoted by nobody. It is pinned to the one
host that serves this repo's bytes, because this repo deploys no website of its
own — see ADR 0008.
* the served `$id` copy is the schema's public identity — checked because a
stale schema that still resolves is worse than one that 404s.
"""
import json
import re
Expand Down
Loading