diff --git a/.github/scripts/downstream-canary-stella.sh b/.github/scripts/downstream-canary-stella.sh index 86eb275..b794a6e 100755 --- a/.github/scripts/downstream-canary-stella.sh +++ b/.github/scripts/downstream-canary-stella.sh @@ -68,6 +68,15 @@ fi echo "CGP crates available to patch in: ${crates[*]}" +# A local patch must still satisfy the downstream's declared semver requirement. +# During a release-major canary, temporarily align exact workspace pins with +# this checkout so the canary tests source compatibility rather than stopping +# at dependency resolution. This only mutates the disposable downstream checkout. +cgp_version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' "$CGP_DIR/Cargo.toml" | head -1) +for crate in "${crates[@]}"; do + sed -i -E "s|^(${crate}[[:space:]]*=[[:space:]]*)\"=?[0-9][^\"]*\"|\1\"=${cgp_version}\"|" "$STELLA_MANIFEST" +done + if grep -qF "$SENTINEL" "$STELLA_MANIFEST"; then echo "stella's Cargo.toml already carries the local-CGP patch — leaving it as-is." else @@ -79,11 +88,16 @@ else for crate in "${crates[@]}"; do printf '%s = { path = "%s/%s" }\n' "$crate" "$CGP_DIR" "$crate" done + echo "" + echo '[patch.crates-io]' + for crate in "${crates[@]}"; do + printf '%s = { path = "%s/%s" }\n' "$crate" "$CGP_DIR" "$crate" + done } >>"$STELLA_MANIFEST" fi echo "--- patched Cargo.toml tail ---" -tail -n "$(( ${#crates[@]} + 3 ))" "$STELLA_MANIFEST" +tail -n "$(( 2 * ${#crates[@]} + 5 ))" "$STELLA_MANIFEST" echo "-------------------------------" # Discover which stella crates depend on a contextgraph-* crate at all, from @@ -93,8 +107,8 @@ echo "-------------------------------" dependents=() while IFS= read -r manifest; do dependents+=("$(basename "$(dirname "$manifest")")") -done < <(cd "$STELLA_DIR" && find . -mindepth 2 -maxdepth 2 -name Cargo.toml \ - -exec grep -lE '^contextgraph-[a-z-]+ = ' {} \; | sort -u) +done < <(cd "$STELLA_DIR" && find . -mindepth 2 -maxdepth 3 -name Cargo.toml \ + -exec grep -lE '^contextgraph-[a-z-]+([.]workspace)?[[:space:]]*=' {} \; | sort -u) if [[ "${#dependents[@]}" -eq 0 ]]; then echo "::error::no stella crate depends on contextgraph-* — is STELLA_DIR stale, or did the dependency move?" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd8602f..f6157ca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,8 @@ name: Release -# Publishes the three publishable Context Graph Protocol crates to crates.io, +# Publishes the four public Context Graph Protocol crates to crates.io, # in dependency order (see PUBLISHING.md): contextgraph-types -> contextgraph-host -# -> contextgraph-conformance. `contextgraph-trace` is deliberately excluded — it -# inherits the workspace's `publish = false` default (see its Cargo.toml). +# -> contextgraph-conformance -> contextgraph-trace. # # This workflow is inert by construction. A `contextgraph-v*` tag push alone # can never publish anything: @@ -54,7 +53,7 @@ jobs: - run: cargo publish --dry-run -p contextgraph-types publish: - name: publish crates.io (contextgraph-types -> contextgraph-host -> contextgraph-conformance) + name: publish crates.io (types -> host -> conformance -> trace) needs: preflight runs-on: ubuntu-latest environment: crates-io @@ -72,7 +71,7 @@ jobs: run: ./.github/scripts/wait-for-crate.sh contextgraph-types "${GITHUB_REF_NAME#contextgraph-v}" # contextgraph-host depends on contextgraph-types via a path dep with a - # ">=0.1.0" version requirement (see contextgraph-host/Cargo.toml) — + # ">=1.0.0" version requirement (see contextgraph-host/Cargo.toml) — # crates.io strips the path and resolves the version req against the # registry, so this step fails fast if the wait above returned too # early. @@ -88,3 +87,8 @@ jobs: run: cargo publish -p contextgraph-conformance --locked env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + - name: Publish contextgraph-trace + run: cargo publish -p contextgraph-trace --locked + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 408d71b..c1d8321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ specification repository are documented in this file. The Context Graph Protocol crates (`contextgraph-types`, `contextgraph-host`, `contextgraph-conformance`, `contextgraph-trace`) track **crate -version** (`0.x` today) and **protocol version** (`contextgraph/1.0-draft`) as two +version** (`1.x` today) and **protocol version** (`contextgraph/1.0`) as two independent axes — see [docs/stability.md](./docs/stability.md). This changelog records crate releases and spec-repository milestones together, noting which is which. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). @@ -47,6 +47,24 @@ this is a crate-version release only. ## [Unreleased] +## [1.0.0] — 2026-08-11 (stable protocol and crate release) + +The `contextgraph/1.0` wire contract is now frozen. The four public Rust crates +move to 1.0.0 in lockstep, and the TypeScript and Python provider SDKs advertise +the stable protocol identifier. Existing `contextgraph/1.0-draft` peers remain +wire-compatible under the major-family negotiation rule. + +### Changed +- Dropped the `-draft` suffix from `PROTOCOL_VERSION` and every reference wire + vector, SDK, example, badge, and conformance fixture. +- Renamed the attested fixture profile to `contextgraph-1.0`; its strict file + coverage and SHA-256 manifest remain the release witness downstreams can pin. +- Promoted workspace crates and published SDK manifests to version `1.0.0`. +- Integrated PR #76's schema/spec regression coverage before the freeze, so + documented JSONC examples and schema `$id` bytes are checked alongside the + existing wire, host, provider, and composition conformance suites. + + ### Added - **First real crates.io publish** (2026-07-31) — `contextgraph-types`, @@ -274,6 +292,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 diff --git a/Cargo.lock b/Cargo.lock index fb03eed..e263509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,7 +211,7 @@ dependencies = [ [[package]] name = "contextgraph-conformance" -version = "0.1.2" +version = "1.0.0" dependencies = [ "async-trait", "clap", @@ -227,7 +227,7 @@ dependencies = [ [[package]] name = "contextgraph-host" -version = "0.1.2" +version = "1.0.0" dependencies = [ "async-trait", "contextgraph-types", @@ -244,7 +244,7 @@ dependencies = [ [[package]] name = "contextgraph-mcp-bridge" -version = "0.1.2" +version = "1.0.0" dependencies = [ "clap", "contextgraph-host", @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "contextgraph-mcp-server" -version = "0.1.2" +version = "1.0.0" dependencies = [ "async-trait", "contextgraph-host", @@ -269,7 +269,7 @@ dependencies = [ [[package]] name = "contextgraph-refprov" -version = "0.1.2" +version = "1.0.0" dependencies = [ "contextgraph-host", "contextgraph-types", @@ -279,7 +279,7 @@ dependencies = [ [[package]] name = "contextgraph-ripgrep" -version = "0.1.2" +version = "1.0.0" dependencies = [ "contextgraph-refprov", "contextgraph-types", @@ -287,7 +287,7 @@ dependencies = [ [[package]] name = "contextgraph-trace" -version = "0.1.2" +version = "1.0.0" dependencies = [ "contextgraph-types", "serde", @@ -297,7 +297,7 @@ dependencies = [ [[package]] name = "contextgraph-treesitter" -version = "0.1.2" +version = "1.0.0" dependencies = [ "contextgraph-refprov", "contextgraph-types", @@ -305,7 +305,7 @@ dependencies = [ [[package]] name = "contextgraph-types" -version = "0.1.2" +version = "1.0.0" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 4ba9c4f..2b3595e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ ] [workspace.package] -version = "0.1.2" +version = "1.0.0" edition = "2024" rust-version = "1.90" license = "MIT OR Apache-2.0" diff --git a/GOVERNANCE.md b/GOVERNANCE.md index f4c09ce..2fde6fd 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,7 +1,7 @@ # Context Graph Protocol governance This document describes how the Context Graph Protocol is maintained, how -it changes, and the path from `contextgraph/1.0-draft` to a frozen `contextgraph/1.0`. It exists +it changes, and the maintenance of the frozen `contextgraph/1.0` family. It exists so adopters can trust that the protocol is maintained deliberately and that "Context Graph Protocol conformant" is a stable, verifiable claim — not a maintainer's mood. @@ -9,7 +9,7 @@ so adopters can trust that the protocol is maintained deliberately and that - **Maintainer.** Mac Anderson (`@macanderson`) is the current maintainer. The maintainer owns release decisions, approval of normative changes, and the - call on when `contextgraph/1.0-draft` freezes to `contextgraph/1.0`. + stewardship of the frozen `contextgraph/1.0` family. - **Contributors.** Anyone. Contributions land via pull request under the [DCO](./CONTRIBUTING.md) — no CLA, no copyright assignment. @@ -51,26 +51,18 @@ version bump. The bias is **additive, not breaking.** A new optional field is a minor change; a removed or renamed field requires a new major family (`contextgraph/2`). -## The path to `contextgraph/1.0` - -`contextgraph/1.0-draft` freezes to `contextgraph/1.0` when **all** of the following are true: - -- **Independent implementations.** At least two independent Context Graph Protocol implementations - pass the conformance suite against the reference host. One reference - implementation plus one genuine third-party implementation is the minimum; - the point is to prove the spec is buildable without the reference code. -- **Stabilization window.** The wire surface has had no normative change for at - least 60 days. -- **No blocking normative issues.** There are no open normative issues the - maintainer considers blocking. -- **Complete enforcement.** The conformance suite's checks are agreed to fully - enforce the documented conformance requirements in [`SPEC.md`](./SPEC.md) - (indexed in [protocol-surface.md](./docs/protocol-surface.md#conformance-requirements)). - -At the freeze, the `-draft` suffix is dropped, the crates move to `1.0.0` in -lockstep, and the major-family compatibility rule guarantees that -already-deployed `1.0-draft` providers keep handshaking successfully — no flag -day. See [stability.md](./docs/stability.md). +## The `contextgraph/1.0` freeze + +The protocol froze on 2026-08-11 after the pre-freeze backlog and conformance +enforcement sweep. The release ships independent TypeScript, Python, Go, and +Rust provider implementations, an external-provider harness, host and custom +composition suites, and an attested fixture profile. Stella remains compatible +with both the former draft identifier and stable peers through major-family +negotiation. + +Within `contextgraph/1`, normative evolution is additive-only. A removed, +renamed, or repurposed wire field requires `contextgraph/2`; conformance checks +may be strengthened when doing so enforces an already-normative requirement. ## Governance evolution diff --git a/README.md b/README.md index dac7ea9..0e24cb1 100644 --- a/README.md +++ b/README.md @@ -166,9 +166,9 @@ prompt. MCP tools do the work. Green means it works with any CGP host. A broken provider is caught at CI time, not at integration time. The suite ships a `--misbehave` mode that trips every check on purpose, so you know the checks are real. -- **Stability you can pin.** The protocol version is `contextgraph/1.0-draft`. Two versions +- **Stability you can pin.** The protocol version is `contextgraph/1.0`. Two versions interoperate when they share a major family, the part before the first dot. So - `contextgraph/1.0-draft` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate. + `contextgraph/1.0` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate. When the draft freezes, every deployed provider keeps working. No flag day. --- @@ -184,7 +184,7 @@ contributing you agree your contributions are licensed under the same terms. ## Status -CGP is `contextgraph/1.0-draft` today. The wire types are stable enough to build against, +CGP is `contextgraph/1.0` today. The wire types are stable enough to build against, the host runtime enforces the guarantees, and the conformance suite verifies them. The path from "open context as an idea" to "open context as a standard" is the conformance suite: anyone can build a provider, anyone can verify it, and the diff --git a/SECURITY.md b/SECURITY.md index 64bcc32..8d8cc29 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -20,7 +20,7 @@ Include as much of the following as you can: - A description of the issue and its security impact. - The Context Graph Protocol crate(s) and version(s) affected (`contextgraph-types`, `contextgraph-host`, `contextgraph-conformance`). -- The protocol version (e.g. `contextgraph/1.0-draft`). +- The protocol version (e.g. `contextgraph/1.0`). - A minimal repro: a malformed envelope, a misbehaving provider, or a bypassed consent gate. - Any mitigations you have identified. @@ -56,5 +56,5 @@ Advisory and, where applicable, a CVE. ## Supported versions -Context Graph Protocol is pre-1.0 (`contextgraph/1.0-draft`). Only the latest published crate release +Context Graph Protocol is pre-1.0 (`contextgraph/1.0`). Only the latest published crate release receives security fixes until the `contextgraph/1.0` freeze. diff --git a/SPEC.md b/SPEC.md index 19a58e4..91e9655 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,6 +1,6 @@ # Context Graph Protocol (CGP) — normative specification -**Version:** `contextgraph/1.0-draft` +**Version:** `contextgraph/1.0` This document is the **single normative home** of the Context Graph Protocol. A provider or host can be implemented from this document, the @@ -95,7 +95,7 @@ minor = 1*DIGIT The **major family** is the substring up to (not including) the first `.`. Two versions interoperate **if and only if** they share a major family. -`contextgraph/1.0-draft` and `contextgraph/1.0` both belong to `contextgraph/1` +`contextgraph/1.0` and `contextgraph/1.0` both belong to `contextgraph/1` and interoperate; `contextgraph/2.0` does not. This is what lets the freeze drop `-draft` without a flag day. An diff --git a/assets/badges/conformant.svg b/assets/badges/conformant.svg index d77adb4..b4c73f7 100644 --- a/assets/badges/conformant.svg +++ b/assets/badges/conformant.svg @@ -1,5 +1,5 @@ - - CGP conformant: contextgraph/1.0-draft + + CGP conformant: contextgraph/1.0 @@ -17,7 +17,7 @@ CGP conformant - contextgraph/1.0-draft - contextgraph/1.0-draft + contextgraph/1.0 + contextgraph/1.0 diff --git a/contextgraph-conformance/Cargo.toml b/contextgraph-conformance/Cargo.toml index 2acc2df..0c6ae08 100644 --- a/contextgraph-conformance/Cargo.toml +++ b/contextgraph-conformance/Cargo.toml @@ -24,8 +24,8 @@ dist = false [dependencies] # Floor requirements — see the note in contextgraph-host/Cargo.toml: caret reqs break # the release version stamp at the first minor bump. -contextgraph-types = { path = "../contextgraph-types", version = ">=0.1.0" } -contextgraph-host = { path = "../contextgraph-host", version = ">=0.1.0" } +contextgraph-types = { path = "../contextgraph-types", version = ">=1.0.0" } +contextgraph-host = { path = "../contextgraph-host", version = ">=1.0.0" } serde.workspace = true serde_json.workspace = true tokio.workspace = true diff --git a/contextgraph-conformance/README.md b/contextgraph-conformance/README.md index b1a0dfb..5c65dde 100644 --- a/contextgraph-conformance/README.md +++ b/contextgraph-conformance/README.md @@ -61,7 +61,7 @@ See [Running conformance][conformance] for the full guide. ## Golden fixtures The versioned interoperability fixtures live under -`fixtures/contextgraph-1.0-draft/`. The profile contains fully populated and +`fixtures/contextgraph-1.0/`. The profile contains fully populated and minimal `ContextFrame` cases, a minimal `ContextQuery`, distinct missing- and blank-citation cases, strict unknown-field negatives, and RFC 8785 JSON Canonicalization Scheme (JCS) normalization vectors. `manifest.json` pins the diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.compact.valid.json b/contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.compact.valid.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.compact.valid.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.compact.valid.json diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.missing-citation.invalid.json b/contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.missing-citation.invalid.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.missing-citation.invalid.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.missing-citation.invalid.json diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.reference.valid.json b/contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.reference.valid.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.reference.valid.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.reference.valid.json diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.valid.json b/contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.valid.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-frame.valid.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/context-frame.valid.json diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-query.valid.json b/contextgraph-conformance/fixtures/contextgraph-1.0/context-query.valid.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/context-query.valid.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/context-query.valid.json diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/manifest.json b/contextgraph-conformance/fixtures/contextgraph-1.0/manifest.json similarity index 95% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/manifest.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/manifest.json index ec129b5..25a32fd 100644 --- a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/manifest.json +++ b/contextgraph-conformance/fixtures/contextgraph-1.0/manifest.json @@ -1,5 +1,5 @@ { - "protocol_version": "contextgraph/1.0-draft", + "protocol_version": "contextgraph/1.0", "fixture_profile_version": "1.1.0", "generation_command": "cargo test -p contextgraph-conformance --test golden_fixtures", "files": { diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/normalization-vectors.json b/contextgraph-conformance/fixtures/contextgraph-1.0/normalization-vectors.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/normalization-vectors.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/normalization-vectors.json diff --git a/contextgraph-conformance/fixtures/contextgraph-1.0-draft/strict-validation.invalid.json b/contextgraph-conformance/fixtures/contextgraph-1.0/strict-validation.invalid.json similarity index 100% rename from contextgraph-conformance/fixtures/contextgraph-1.0-draft/strict-validation.invalid.json rename to contextgraph-conformance/fixtures/contextgraph-1.0/strict-validation.invalid.json diff --git a/contextgraph-conformance/tests/golden_fixtures.rs b/contextgraph-conformance/tests/golden_fixtures.rs index ea6a583..c2bf51f 100644 --- a/contextgraph-conformance/tests/golden_fixtures.rs +++ b/contextgraph-conformance/tests/golden_fixtures.rs @@ -10,7 +10,7 @@ use serde::Deserialize; use serde_json::{Map, Value}; use sha2::{Digest, Sha256}; -const PROFILE: &str = "contextgraph-1.0-draft"; +const PROFILE: &str = "contextgraph-1.0"; const PROFILE_VERSION: &str = "1.1.0"; const GENERATION_COMMAND: &str = "cargo test -p contextgraph-conformance --test golden_fixtures"; const FIXTURE_FILES: [&str; 7] = [ @@ -22,7 +22,7 @@ const FIXTURE_FILES: [&str; 7] = [ "normalization-vectors.json", "strict-validation.invalid.json", ]; -// The pinned `contextgraph-1.0-draft` strict frame profile. `content_digest` is +// The pinned `contextgraph-1.0` strict frame profile. `content_digest` is // intentionally excluded here as it was before the representation work; the nine // representation/cost fields below are additive and default-absent. const FRAME_FIELDS: [&str; 23] = [ @@ -501,7 +501,7 @@ fn representation_vectors_are_honest_and_structurally_valid() { // // These are parsed as real `ContextFrame`s rather than through // `strict_frame`: a `compact` frame carries `content_digest`, which the - // frozen `contextgraph-1.0-draft` field allow-list deliberately omits (it + // frozen `contextgraph-1.0` field allow-list deliberately omits (it // predates the representation work). Their conformance is proven below by // `representation_invariants`, §B3 honesty, and `check_frames`. let compact: ContextFrame = read_fixture("context-frame.compact.valid.json"); diff --git a/contextgraph-host/Cargo.toml b/contextgraph-host/Cargo.toml index 747ca9f..d890384 100644 --- a/contextgraph-host/Cargo.toml +++ b/contextgraph-host/Cargo.toml @@ -21,7 +21,7 @@ publish = true # stops matching at the first minor bump (path deps must satisfy `version` # too). ">=" keeps the stamped build resolving at any version and remains a # valid crates.io requirement for the independent Context Graph Protocol publish (PUBLISHING.md). -contextgraph-types = { path = "../contextgraph-types", version = ">=0.1.0" } +contextgraph-types = { path = "../contextgraph-types", version = ">=1.0.0" } serde.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/contextgraph-host/src/wire.rs b/contextgraph-host/src/wire.rs index b09058f..f4ef06b 100644 --- a/contextgraph-host/src/wire.rs +++ b/contextgraph-host/src/wire.rs @@ -220,7 +220,7 @@ pub fn decode_line(line: &str) -> Result { /// Two protocol version strings interoperate when they share a **major /// family** — the substring up to the first `.`. So `contextgraph/1.0-draft` and /// `contextgraph/1.0` interoperate (both `contextgraph/1`), while `contextgraph/2.0` does not. This is -/// what lets the public v1.0 freeze drop the `-draft` suffix without a flag +/// what allowed the public v1.0 freeze to drop the `-draft` suffix without a flag /// day (`SPEC.md`). pub fn versions_compatible(a: &str, b: &str) -> bool { protocol_family(a) == protocol_family(b) @@ -359,14 +359,11 @@ mod tests { "contextgraph/1.0" )); assert!(versions_compatible( - "contextgraph/1.0-draft", + "contextgraph/1.0", "contextgraph/1.0-draft" )); assert!(versions_compatible(PROTOCOL_VERSION, "contextgraph/1.9")); - assert!(!versions_compatible( - "contextgraph/1.0-draft", - "contextgraph/2.0" - )); + assert!(!versions_compatible("contextgraph/1.0", "contextgraph/2.0")); assert!(!versions_compatible("contextgraph/1.0", "mcp/1.0")); } } diff --git a/contextgraph-mcp-bridge/Cargo.toml b/contextgraph-mcp-bridge/Cargo.toml index 3b6ce14..3aa19de 100644 --- a/contextgraph-mcp-bridge/Cargo.toml +++ b/contextgraph-mcp-bridge/Cargo.toml @@ -11,10 +11,10 @@ homepage.workspace = true publish = false [dependencies] -contextgraph-types = { path = "../contextgraph-types", version = ">=0.1.0" } +contextgraph-types = { path = "../contextgraph-types", version = ">=1.0.0" } # Reused only for the wire `Envelope` enum + its NDJSON codec, so the bridge's # CGP side stays byte-for-byte the reference wire rather than a re-spelling. -contextgraph-host = { path = "../contextgraph-host", version = ">=0.1.0" } +contextgraph-host = { path = "../contextgraph-host", version = ">=1.0.0" } serde.workspace = true serde_json.workspace = true sha2.workspace = true diff --git a/contextgraph-mcp-server/Cargo.toml b/contextgraph-mcp-server/Cargo.toml index 343f558..8fabc40 100644 --- a/contextgraph-mcp-server/Cargo.toml +++ b/contextgraph-mcp-server/Cargo.toml @@ -11,8 +11,8 @@ homepage.workspace = true publish = false [dependencies] -contextgraph-types = { path = "../contextgraph-types", version = ">=0.1.0" } -contextgraph-host = { path = "../contextgraph-host", version = ">=0.1.0" } +contextgraph-types = { path = "../contextgraph-types", version = ">=1.0.0" } +contextgraph-host = { path = "../contextgraph-host", version = ">=1.0.0" } serde.workspace = true serde_json.workspace = true tokio.workspace = true diff --git a/contextgraph-trace/Cargo.toml b/contextgraph-trace/Cargo.toml index e93cd00..503c55f 100644 --- a/contextgraph-trace/Cargo.toml +++ b/contextgraph-trace/Cargo.toml @@ -26,7 +26,7 @@ publish = true [dependencies] # Floor requirement — see the note in contextgraph-host/Cargo.toml: caret reqs # break the release version stamp at the first minor bump. -contextgraph-types = { path = "../contextgraph-types", version = ">=0.1.0" } +contextgraph-types = { path = "../contextgraph-types", version = ">=1.0.0" } serde.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/contextgraph-types/README.md b/contextgraph-types/README.md index 553ca49..50f0f93 100644 --- a/contextgraph-types/README.md +++ b/contextgraph-types/README.md @@ -11,7 +11,7 @@ licensed, zero dependencies beyond `serde`.** You can implement a CGP provider or host in Rust against this crate alone, with no dependency on [Stella](https://github.com/macanderson/stella) or any of its other crates. -Protocol version: `contextgraph/1.0-draft` (see [`stability.md`][stability] for what +Protocol version: `contextgraph/1.0` (see [`stability.md`][stability] for what that means for this crate's semver). ## What's in here diff --git a/contextgraph-types/src/lib.rs b/contextgraph-types/src/lib.rs index b59e6b5..c443334 100644 --- a/contextgraph-types/src/lib.rs +++ b/contextgraph-types/src/lib.rs @@ -8,7 +8,7 @@ //! //! [`SPEC.md`]: https://github.com/macanderson/context-graph-protocol/blob/main/SPEC.md //! -//! Protocol version: `contextgraph/1.0-draft`. +//! Protocol version: `contextgraph/1.0`. pub mod attribution; pub mod capability; @@ -53,6 +53,5 @@ pub use validate::{ }; pub use verify::{FrameVerdict, Verdict, VerifyRequest, VerifyResponse}; -/// The protocol version string this crate implements. Frozen to `contextgraph/1.0` -/// only at the public v1.0 release (`SPEC.md` §Version strings). -pub const PROTOCOL_VERSION: &str = "contextgraph/1.0-draft"; +/// The stable protocol version string this crate implements (`SPEC.md` §3.1). +pub const PROTOCOL_VERSION: &str = "contextgraph/1.0"; diff --git a/contextgraph-types/src/record.rs b/contextgraph-types/src/record.rs index e219205..59791a1 100644 --- a/contextgraph-types/src/record.rs +++ b/contextgraph-types/src/record.rs @@ -38,7 +38,7 @@ use crate::validate::{is_protocol_timestamp, is_well_formed_digest}; /// The profile version every `ContextRecord.schema_version` names. Distinct /// from the wire [`PROTOCOL_VERSION`](crate::PROTOCOL_VERSION) -/// (`contextgraph/1.0-draft`): the lifecycle layer is a *profile* on top of the +/// (`contextgraph/1.0`): the lifecycle layer is a *profile* on top of the /// base family (ADR 0007 §5, reconciliation row D4), so it version-stamps /// itself rather than riding the core version. pub const LIFECYCLE_SCHEMA_VERSION: &str = "contextgraph/lifecycle/1.0-draft"; @@ -722,7 +722,7 @@ mod tests { statement: "x".into(), subject_ref: None, }); - record.schema_version = "contextgraph/1.0-draft".into(); + record.schema_version = "contextgraph/1.0".into(); assert!(record.envelope_invariants().is_err()); let mut record = record_with(RecordBody::Observation { diff --git a/docs/adaptive-context-reconciliation.md b/docs/adaptive-context-reconciliation.md index 042d802..19f948c 100644 --- a/docs/adaptive-context-reconciliation.md +++ b/docs/adaptive-context-reconciliation.md @@ -94,7 +94,7 @@ holding editable local copies plus CGEP naming. | # | Item | Bundle says | CGP position | Class | Destination | |---|---|---|---|---|---| -| E1 | **Rename to "Context Graph Exchange Protocol / CGEP"**, `cgep/1.0-draft` namespace, `context-graph-exchange-protocol` repo (BP §naming; oxagen lifecycle §23; rationale: "AgentSpeak uses Context Graph Protocol") | Rename the protocol. | Canonical name is **Context Graph Protocol (CGP)**; wire `contextgraph/1.0-draft`; stem `contextgraph`. Owner confirmed 2026-07-23. | **reject** | ADR 0007 §5. Every adopted BP item is normalized to CGP naming. | +| E1 | **Rename to "Context Graph Exchange Protocol / CGEP"**, `cgep/1.0-draft` namespace, `context-graph-exchange-protocol` repo (BP §naming; oxagen lifecycle §23; rationale: "AgentSpeak uses Context Graph Protocol") | Rename the protocol. | Canonical name is **Context Graph Protocol (CGP)**; wire `contextgraph/1.0`; stem `contextgraph`. Owner confirmed 2026-07-23. | **reject** | ADR 0007 §5. Every adopted BP item is normalized to CGP naming. | | E2 | **Portable `project_id` in scope** (DS draft) | Add to portable scope. | BP itself forbids it ("do not add `project_id` to the portable core until there is a cross-provider registry contract"); oxagen marks the draft superseded. | **reject (defer)** | Not portable until a registry contract exists. Host may key on project internally. | | E3 | **9-key `Scope`** (tenant/org/workspace/project/repo/env/session/task/user) (FS) vs BP's 7-key portable scope + `sharing_scope` | Frame scope. | CGP query scope differs; portable record scope belongs to the profile. | **adopt-upstream (issue)** / reconcile | **RESOLVED (#28)** — [profile](./profiles/context-exchange-provider.md) §5.1 LS1–LS3: portable `scope` is the 7-key `{user_id, organization_id, repository_id, workspace_id, environment_id, session_id, task_id}` + `sharing_scope: user\|repository\|workspace\|organization`, all conjunctive; `tenant_id`/`project_id` dropped from the portable core (schema rejects them). | | E4 | **`context/propose`, `context/promote`, `context/validate` operations** | (BP explicitly says do **not** expose these.) | Agree — policy-executing operations are host-only; the protocol records decisions after the host makes them. | **reject** | Recorded as a boundary invariant (ADR 0007 §3). | diff --git a/docs/adr/0005-frame-representations.md b/docs/adr/0005-frame-representations.md index e9b57c0..3df232b 100644 --- a/docs/adr/0005-frame-representations.md +++ b/docs/adr/0005-frame-representations.md @@ -1,6 +1,6 @@ # 0005 — Frame representations (full, compact, reference) -**Status:** Accepted (draft; `contextgraph/1.0-draft`) +**Status:** Accepted (draft; `contextgraph/1.0`) **Context:** CGEP lifecycle work, phase 2 — frame representations. Numbering is provisional: this ADR lands ahead of PR #33's ADR set (0002–0004); if it diff --git a/docs/adr/0006-prompt-ingestion-as-a-local-provider.md b/docs/adr/0006-prompt-ingestion-as-a-local-provider.md index 8768466..5483007 100644 --- a/docs/adr/0006-prompt-ingestion-as-a-local-provider.md +++ b/docs/adr/0006-prompt-ingestion-as-a-local-provider.md @@ -1,6 +1,6 @@ # 0006 — Prompt ingestion as a local provider -**Status:** Accepted (draft; `contextgraph/1.0-draft`) +**Status:** Accepted (draft; `contextgraph/1.0`) ## Context diff --git a/docs/adr/0007-protocol-product-boundary.md b/docs/adr/0007-protocol-product-boundary.md index 71f4cc7..e290c27 100644 --- a/docs/adr/0007-protocol-product-boundary.md +++ b/docs/adr/0007-protocol-product-boundary.md @@ -1,6 +1,6 @@ # 0007 — The protocol/product boundary: atomic `ContextFrame` vs host-owned `CompiledContextFrame` -**Status:** Accepted (draft; `contextgraph/1.0-draft`) +**Status:** Accepted (draft; `contextgraph/1.0`) ## Context @@ -116,7 +116,7 @@ The stella build prompt and the oxagen lifecycle spec proposed renaming the protocol to "Context Graph **Exchange** Protocol / CGEP", a `cgep/1.0-draft` capability namespace, and a `context-graph-exchange-protocol` repo. **This is rejected.** The canonical name is **Context Graph Protocol (CGP)**; the wire -version is **`contextgraph/1.0-draft`**; the capability/namespace stem is +version is **`contextgraph/1.0`**; the capability/namespace stem is **`contextgraph`**. Any item adopted from the build prompt is normalized to this naming. (Public docs spell out "Context Graph Protocol (CGP)" on first use, then use "CGP" freely.) diff --git a/docs/context-reuse.md b/docs/context-reuse.md index af9e81c..3d2d3de 100644 --- a/docs/context-reuse.md +++ b/docs/context-reuse.md @@ -31,7 +31,7 @@ dependency run top to bottom. > `contextgraph/1` family: new optional fields (`content_digest`, > `egress_scopes`), new capability-gated methods (`verify`), and new host-side > artifacts (`UsageReport`, `ConsentReceipt`) that ride no new required wire -> field. A `contextgraph/1.0-draft` provider that implements none of them still +> field. A `contextgraph/1.0` provider that implements none of them still > handshakes and answers queries; a host that wants them degrades to its > existing behavior (re-query, boolean consent) when a provider opts out. No > flag day — see [stability.md](./stability.md). diff --git a/docs/implementing-a-provider.md b/docs/implementing-a-provider.md index 1c40a53..223d85a 100644 --- a/docs/implementing-a-provider.md +++ b/docs/implementing-a-provider.md @@ -117,7 +117,7 @@ hang; but only your provider can guarantee it survives garbage input. ### Version compatibility Two protocol version strings interoperate when they share a **major -family** — the substring up to the first `.`. So `contextgraph/1.0-draft` and +family** — the substring up to the first `.`. So `contextgraph/1.0` and `contextgraph/1.0` interoperate (both family `contextgraph/1`), while `contextgraph/2.0` does not (`contextgraph-host::wire::versions_compatible`). This is what lets the eventual public `contextgraph/1.0` freeze drop the `-draft` suffix without a flag day — ack diff --git a/docs/index.md b/docs/index.md index 894ea49..725369e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -53,7 +53,7 @@ Reference documentation for the **Context Graph Protocol (CGP)** crates: and how to get your own provider listed. - [**Stability**](./stability.md) — the crate-semver vs. protocol-version relationship, and what changes (and doesn't) as the protocol moves from - `contextgraph/1.0-draft` to `contextgraph/1.0`. + `contextgraph/1.0` to `contextgraph/1.0`. Also at the repo root: [`GOVERNANCE.md`](../GOVERNANCE.md) (how the protocol is maintained, what counts as a normative change, and the path to `contextgraph/1.0`), diff --git a/docs/overview.md b/docs/overview.md index b8df2fd..7e8f2f3 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -159,16 +159,16 @@ prompt. MCP tools do the work. Green means it works with any CGP host. A broken provider is caught at CI time, not at integration time. The suite ships a `--misbehave` mode that trips every check on purpose, so you know the checks are real. -- **Stability you can pin.** The protocol version is `contextgraph/1.0-draft`. Two versions +- **Stability you can pin.** The protocol version is `contextgraph/1.0`. Two versions interoperate when they share a major family, the part before the first dot. So - `contextgraph/1.0-draft` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate. + `contextgraph/1.0` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate. When the draft freezes, every deployed provider keeps working. No flag day. --- ## Status -CGP is `contextgraph/1.0-draft` today. The wire types are stable enough to build against, +CGP is `contextgraph/1.0` today. The wire types are stable enough to build against, the host runtime enforces the guarantees, and the conformance suite verifies them. The path from "open context as an idea" to "open context as a standard" is the conformance suite: anyone can build a provider, anyone can verify it, and the diff --git a/docs/protocol-advantages.md b/docs/protocol-advantages.md index 62d74b8..21dbac4 100644 --- a/docs/protocol-advantages.md +++ b/docs/protocol-advantages.md @@ -287,10 +287,10 @@ target id. The convention is consistent across the protocol surface. CGP separates **crate version** (ordinary Cargo semver) from **protocol version** (the wire-format identity negotiated at handshake). The current -protocol version is `contextgraph/1.0-draft`. +protocol version is `contextgraph/1.0`. Two protocol versions interoperate when they share a **major family** — the -substring up to the first `.`. So `contextgraph/1.0-draft` and `contextgraph/1.0` both belong to +substring up to the first `.`. So `contextgraph/1.0` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate. A jump to `contextgraph/2.0` does not. This design has a critical consequence: **the freeze from draft to stable does diff --git a/docs/protocol-surface.md b/docs/protocol-surface.md index 86f4fd7..d1d3ab1 100644 --- a/docs/protocol-surface.md +++ b/docs/protocol-surface.md @@ -13,7 +13,7 @@ below lives in that crate, round-trips through `serde_json`, and *is* the protocol — there is no separate IDL. Field-level doc comments in the crate are the ultimate source of truth; this page is a guided tour. -Protocol version: `PROTOCOL_VERSION = "contextgraph/1.0-draft"` (`contextgraph-types/src/lib.rs`). +Protocol version: `PROTOCOL_VERSION = "contextgraph/1.0"` (`contextgraph-types/src/lib.rs`). See [`stability.md`](./stability.md) for what "draft" means and when it freezes. @@ -408,10 +408,10 @@ minor = 1*DIGIT ``` The **major family** is the substring up to (but not including) the first `.` -— e.g. the family of `contextgraph/1.0-draft` is `contextgraph/1`. +— e.g. the family of `contextgraph/1.0` is `contextgraph/1`. Two version strings interoperate if and only if they share a major family. -`contextgraph/1.0-draft` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate; +`contextgraph/1.0` and `contextgraph/1.0` both belong to family `contextgraph/1` and interoperate; `contextgraph/2.0` does not interoperate with either. The `-draft` suffix marks a not-yet-frozen version within a family and does not affect interoperability. This rule is implemented by `contextgraph-host::wire::versions_compatible`; an diff --git a/docs/registry.md b/docs/registry.md index 31ff7f5..4f18d6d 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -8,7 +8,7 @@ checkable report backing the claim. It exists so "conformant" stays a verifiable fact about a specific build, not a badge anyone can paste in. Listings here are also load-bearing for governance: the freeze from -`contextgraph/1.0-draft` to `contextgraph/1.0` requires **at least two +`contextgraph/1.0` to `contextgraph/1.0` requires **at least two independent implementations** passing the suite ([GOVERNANCE.md](../GOVERNANCE.md#the-path-to-contextgraph10)). This registry is where that count becomes checkable. @@ -17,7 +17,7 @@ is where that count becomes checkable. | Provider | Author | Transport | Declared capabilities | Data flow | Protocol version | Last verified | Report | |---|---|---|---|---|---|---|---| -| [`contextgraph-example-docs`](../contextgraph-conformance/src/bin/contextgraph-example-docs.rs) | CGP maintainers (bundled reference fixture) | stdio | `kinds=[doc, snippet]`, `graph`, `verify`, `correlation`, `embeddings_fingerprint=bge-small-en-v1.5/384/l2` | reads-only, `egress=false` (`local-only`) | `contextgraph/1.0-draft` | 2026-07-29 | 13/13 checks passed — [report](../registry/contextgraph-example-docs.report.json) | +| [`contextgraph-example-docs`](../contextgraph-conformance/src/bin/contextgraph-example-docs.rs) | CGP maintainers (bundled reference fixture) | stdio | `kinds=[doc, snippet]`, `graph`, `verify`, `correlation`, `embeddings_fingerprint=bge-small-en-v1.5/384/l2` | reads-only, `egress=false` (`local-only`) | `contextgraph/1.0` | 2026-07-29 | 13/13 checks passed — [report](../registry/contextgraph-example-docs.report.json) | This founding entry is the reference fixture bundled with `contextgraph-conformance` itself (`SPEC.md` §11 seed providers) — it exists to @@ -73,7 +73,7 @@ which renders as: The badge is a static, hand-authored asset — not a live third-party redirect — so it never depends on anyone's site being up, and never phones home. It names -the protocol family the badge claims (`contextgraph/1.0-draft`), not a specific +the protocol family the badge claims (`contextgraph/1.0`), not a specific provider version; the row in this table is what backs the specific claim. The URL is this repository's GitHub-raw path because this repository does not diff --git a/docs/stability.md b/docs/stability.md index bd758cf..4fbceec 100644 --- a/docs/stability.md +++ b/docs/stability.md @@ -1,100 +1,52 @@ # Version & stability -Context Graph Protocol (CGP) has **two independent version axes**, and it's important not to conflate -them: +Context Graph Protocol (CGP) has two independent version axes: -- **The crate version** — `0.1.0` today, `[workspace.package].version` in the - workspace root `Cargo.toml`, inherited by `contextgraph-types`, `contextgraph-host`, and - `contextgraph-conformance` alike. This is ordinary Rust/Cargo semver. -- **The protocol version** — `contextgraph/1.0-draft`, the `PROTOCOL_VERSION` constant - in `contextgraph-types::lib`. This is the wire-format identity two CGP - implementations negotiate at handshake time, independent of what language - or crate version either side is written in. +- **Crate version:** `1.0.0`, inherited from the workspace `Cargo.toml` by the + public Rust crates. This follows ordinary semantic versioning. +- **Protocol version:** `contextgraph/1.0`, exposed as + `contextgraph_types::PROTOCOL_VERSION` and negotiated on the wire. -A crate patch release (bug fix, better error message, an added helper -method) does not imply a protocol change. A protocol change, conversely, is -what actually breaks interop between a host and a provider built against -different crate versions — that's the one that matters most to a third -party. +A crate patch can improve implementation behavior without changing the wire +protocol. A protocol-breaking change requires a new protocol and crate major. -## What `-draft` means right now +## The 1.0 stability guarantee -Quoting `contextgraph-types::PROTOCOL_VERSION`'s doc comment verbatim, since it's the -authoritative statement: +The `contextgraph/1.0` wire contract is frozen. Within the `contextgraph/1` +family, changes are additive: defined fields are not removed, renamed, or +repurposed; receivers continue to follow the extensibility rules in SPEC §13. +Rust crates follow semver: `1.x` releases preserve public compatibility, while +a breaking redesign requires both `contextgraph/2.0` and crate version `2.0.0`. -> The protocol version string this crate implements. Frozen to `contextgraph/1.0` -> only at the public v1.0 release. +The former `contextgraph/1.0-draft` identifier belongs to the same major family +and remains wire-compatible. This deliberate compatibility means existing +Stella deployments and other draft-era providers can migrate without a flag +day. New implementations should emit `contextgraph/1.0`. -In other words: the wire shape captured in this `0.1.0` release — -`ContextFrame`, `ContextQuery`, `Capabilities`, the `Envelope` vocabulary, the -consent/budget/citation contracts documented in -[protocol-surface.md](./protocol-surface.md) — is **real and implemented -today** by the reference host (`contextgraph-host`) and conformance suite -(`contextgraph-conformance`), and is safe to build against. It is not yet a **frozen** -contract: a pre-1.0 revision could still change a field shape or add a -required check based on real-world provider implementation feedback, before -the public `contextgraph/1.0` release drops the `-draft` suffix. +## Version-family negotiation -The [context-reuse guarantees](./context-reuse.md) (deterministic composition, -usage reports, consent receipts, `context/verify`) are a worked example of how -the wire shape grows *additively* within a family: they add only optional -fields (`content_digest`, `egress_scopes`), a capability-gated method -(`verify`), and host-side artifacts that ride no new required wire field. A -`contextgraph/1.0-draft` provider that implements none of them still handshakes -and answers queries within family `contextgraph/1`; a host that wants them -degrades to re-query and boolean consent when a provider opts out. That is the -draft-family additive discipline in practice — no flag day, no deployed -provider broken. +`contextgraph_host::wire::versions_compatible` compares the major-family prefix +through the protocol major. Consequently `contextgraph/1.0-draft`, +`contextgraph/1.0`, and future additive `contextgraph/1.x` revisions +interoperate; `contextgraph/2.0` does not. -## Why version families interoperate +## Conformance -`contextgraph-host::wire::versions_compatible` treats two protocol strings as -compatible when they share a **major family** — the substring up to the -first `.`. `contextgraph/1.0-draft` and `contextgraph/1.0` are both family `contextgraph/1` and -interoperate; `contextgraph/2.0` does not interoperate with either. This is -deliberate: it means the eventual freeze from `contextgraph/1.0-draft` to `contextgraph/1.0` -does not require a flag day where every already-deployed provider breaks the -instant the spec freezes — a `1.0-draft` provider and a `1.0` host (or vice -versa) still handshake successfully within the `1` family. What *does* break -interop is a jump to a new major protocol family (`contextgraph/2.0`), which is -reserved for a genuinely breaking protocol redesign. +"CGP conformant" means green on `contextgraph-conformance` for the declared +capability set. Providers should run the suite on every implementation change; +hosts with custom composition should also run the host and composition suites. +The attested `contextgraph-1.0` fixture bundle keeps schema, Rust serialization, +and external implementations tied to the stable contract. -## The stability guarantee, going forward +## Dependency guidance -- **Pre-1.0 (now):** crate versions are `0.x`, tracking `contextgraph/1.0-draft`. Cargo - semver rules mean **any `0.x → 0.y` bump may contain breaking changes** to - either the Rust API or the wire shape — normal pre-1.0 Rust convention. - Pin an exact version (`contextgraph-types = "=0.1.0"`) if you need a hard guarantee - against churn before the freeze. -- **At the freeze:** when the protocol is declared `contextgraph/1.0` (the `-draft` - suffix drops), `contextgraph-types`, `contextgraph-host`, and `contextgraph-conformance` bump to - `1.0.0` in lockstep. That `1.0.0` release is the stability guarantee: from - that point on, the crates follow ordinary semver — a `1.x → 1.y` minor is - additive-only, and a wire-breaking protocol change requires both a new - protocol major (`contextgraph/2.0`) and a new crate major (`2.0.0`). -- **Conformance is the enforcement mechanism.** "CGP conformant" is defined - as green on `contextgraph-conformance`'s suite for your declared capability set - (see [running-conformance.md](./running-conformance.md)) — that suite, not - a hand-audited checklist, is what a third party checks their implementation - against, before and after the freeze alike. - -## Practical guidance for early adopters - -- **Depend on `contextgraph-types` with a caret or exact pin**, per your risk - tolerance — `^0.1` accepts any pre-1.0 patch/minor per Cargo's (unusual) - 0.x semver rules, `=0.1.0` pins exactly. -- **Re-run `contextgraph-conformance` after every `contextgraph-types`/`contextgraph-host` upgrade** - before the 1.0 freeze — a 0.x bump is exactly the kind of change that can - silently add or tighten a conformance check. -- **Don't hardcode `"contextgraph/1.0-draft"` or `"contextgraph/1.0"` in your own handshake - code** — read `contextgraph_types::PROTOCOL_VERSION` and use - `contextgraph_host::wire::versions_compatible` (or the equivalent major-family - comparison, if you're implementing a provider outside Rust) so your - implementation keeps working across the freeze without a code change. +Use a compatible stable requirement such as `contextgraph-types = "1"`, and +upgrade within `1.x` normally. Do not hardcode a protocol identifier: use +`contextgraph_types::PROTOCOL_VERSION` and +`contextgraph_host::wire::versions_compatible`, or implement the equivalent +major-family comparison in another language. ## MSRV and edition -All three crates inherit `rust-version = "1.90"` and `edition = "2024"` from -the workspace. An MSRV bump is a minor-version-worthy change while pre-1.0 -(consistent with the guidance above); after 1.0.0 it will follow the same -semver discipline as the rest of the crate's public API. +The Rust crates use `rust-version = "1.90"` and edition 2024. An MSRV increase +will be handled as a semver-significant compatibility decision. diff --git a/examples/README.md b/examples/README.md index 1b64048..009e8e4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -57,7 +57,7 @@ Labels show direction; they are **not** part of the wire data. speaks. ```json -{"type":"handshake","protocol_version":"contextgraph/1.0-draft"} +{"type":"handshake","protocol_version":"contextgraph/1.0"} ``` **2. provider → host — `handshake_ack`.** The provider replies with its own @@ -65,7 +65,7 @@ version, its identity, and its capabilities. This provider reads workspace content locally and has **no egress**, so a host may auto-enable it. ```json -{"type":"handshake_ack","protocol_version":"contextgraph/1.0-draft","provider":{"name":"repo-graph","version":"0.2.0","data_flow":{"reads":true,"writes":false,"egress":false}},"capabilities":{"query":{"kinds":["doc","symbol"]},"correlation":true,"graph":true,"embeddings_fingerprint":null,"verify":true}} +{"type":"handshake_ack","protocol_version":"contextgraph/1.0","provider":{"name":"repo-graph","version":"0.2.0","data_flow":{"reads":true,"writes":false,"egress":false}},"capabilities":{"query":{"kinds":["doc","symbol"]},"correlation":true,"graph":true,"embeddings_fingerprint":null,"verify":true}} ``` **3. host → provider — `query`.** A retrieval request carrying a hard token @@ -96,7 +96,7 @@ If a provider sends data off the local machine — a cloud documentation search, a remote embedding API — it declares `egress: true`: ```json -{"type":"handshake_ack","protocol_version":"contextgraph/1.0-draft","provider":{"name":"cloud-docs","version":"1.4.0","data_flow":{"reads":true,"writes":false,"egress":true}},"capabilities":{"query":{"kinds":["doc"]},"correlation":true,"graph":false,"embeddings_fingerprint":null}} +{"type":"handshake_ack","protocol_version":"contextgraph/1.0","provider":{"name":"cloud-docs","version":"1.4.0","data_flow":{"reads":true,"writes":false,"egress":true}},"capabilities":{"query":{"kinds":["doc"]},"correlation":true,"graph":false,"embeddings_fingerprint":null}} ``` A conforming host **does not auto-enable** this provider. It gates the provider diff --git a/examples/full-stdio-session.ndjson b/examples/full-stdio-session.ndjson index e52c362..8a0d674 100644 --- a/examples/full-stdio-session.ndjson +++ b/examples/full-stdio-session.ndjson @@ -1,5 +1,5 @@ -{"type": "handshake", "protocol_version": "contextgraph/1.0-draft"} -{"type": "handshake_ack", "protocol_version": "contextgraph/1.0-draft", "provider": {"name": "repo-graph", "version": "0.2.0", "data_flow": {"reads": true, "writes": false, "egress": false}}, "capabilities": {"query": {"kinds": ["doc", "symbol"]}, "correlation": true, "graph": true, "embeddings_fingerprint": null, "verify": true}} +{"type": "handshake", "protocol_version": "contextgraph/1.0"} +{"type": "handshake_ack", "protocol_version": "contextgraph/1.0", "provider": {"name": "repo-graph", "version": "0.2.0", "data_flow": {"reads": true, "writes": false, "egress": false}}, "capabilities": {"query": {"kinds": ["doc", "symbol"]}, "correlation": true, "graph": true, "embeddings_fingerprint": null, "verify": true}} {"type": "query", "query": {"goal": "how do I configure the retry policy?", "query_text": "retry policy configuration", "kinds": ["doc", "symbol"], "anchors": ["src/config.rs"], "max_frames": 5, "max_tokens": 1024}, "id": "q1"} {"type": "frames", "result": {"frames": [{"id": "repo-graph:retry-doc", "kind": "doc", "title": "Retry policy", "content": "Retry behavior is set in Config::retry. max_attempts bounds the tries; backoff_ms is the initial delay, doubled each attempt.", "content_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "uri": "file:///repo/docs/retry.md", "score": 0.92, "token_cost": 32, "provenance": [{"type": "file", "uri": "file:///repo/docs/retry.md", "range": "L1-20", "digest": "sha256:0101010101010101010101010101010101010101010101010101010101010101", "method": "file-read", "by": "repo-graph"}], "citation_label": "retry.md L1-20", "relations": [], "recorded_at": "2026-07-20T18:00:00Z"}, {"id": "repo-graph:retry-sym", "kind": "symbol", "title": "Config::retry", "content": "pub struct RetryPolicy { pub max_attempts: u32, pub backoff_ms: u64 }", "content_digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "uri": "file:///repo/src/config.rs", "score": 0.81, "token_cost": 18, "provenance": [{"type": "file", "uri": "file:///repo/src/config.rs", "range": "L42-44", "digest": "sha256:0808080808080808080808080808080808080808080808080808080808080808", "method": "tree-sitter-symbol-extraction", "by": "repo-graph"}], "citation_label": "config.rs L42-44", "relations": [], "recorded_at": "2026-07-20T18:00:00Z"}], "truncated": false}, "id": "q1"} {"type": "verify", "request": {"frames": [{"provider_id": "repo-graph", "frame_id": "repo-graph:retry-doc", "content_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, {"provider_id": "repo-graph", "frame_id": "repo-graph:retry-sym", "content_digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]}} diff --git a/examples/reference-messages.json b/examples/reference-messages.json index 5584712..84defd4 100644 --- a/examples/reference-messages.json +++ b/examples/reference-messages.json @@ -1,11 +1,11 @@ [ { "type": "handshake", - "protocol_version": "contextgraph/1.0-draft" + "protocol_version": "contextgraph/1.0" }, { "type": "handshake_ack", - "protocol_version": "contextgraph/1.0-draft", + "protocol_version": "contextgraph/1.0", "provider": { "name": "repo-graph", "version": "0.2.0", @@ -30,7 +30,7 @@ }, { "type": "handshake_ack", - "protocol_version": "contextgraph/1.0-draft", + "protocol_version": "contextgraph/1.0", "provider": { "name": "cloud-docs", "version": "1.4.0", @@ -177,7 +177,7 @@ }, { "type": "handshake_ack", - "protocol_version": "contextgraph/1.0-draft", + "protocol_version": "contextgraph/1.0", "provider": { "name": "cloud-docs", "version": "1.3.0", diff --git a/schema/reference-vectors.ndjson b/schema/reference-vectors.ndjson index c990167..fb0de17 100644 --- a/schema/reference-vectors.ndjson +++ b/schema/reference-vectors.ndjson @@ -1,6 +1,6 @@ -{"type":"handshake","protocol_version":"contextgraph/1.0-draft"} -{"type":"handshake_ack","protocol_version":"contextgraph/1.0-draft","provider":{"name":"minimal-provider","version":"0.1.0","data_flow":{"reads":false,"writes":false,"egress":false}},"capabilities":{"query":{"kinds":[]},"correlation":false,"graph":false,"verify":false,"resolve":false}} -{"type":"handshake_ack","protocol_version":"contextgraph/1.0-draft","provider":{"name":"example-docs","version":"1.0.0","data_flow":{"reads":true,"writes":true,"egress":true,"egress_scopes":["org-tenant","third-party-model","acme:vector-store"]}},"capabilities":{"query":{"kinds":["doc","snippet"]},"correlation":true,"graph":true,"embeddings_fingerprint":"bge-small-en-v1.5/384/l2","verify":true,"representations":["full","compact","reference"],"resolve":true}} +{"type":"handshake","protocol_version":"contextgraph/1.0"} +{"type":"handshake_ack","protocol_version":"contextgraph/1.0","provider":{"name":"minimal-provider","version":"0.1.0","data_flow":{"reads":false,"writes":false,"egress":false}},"capabilities":{"query":{"kinds":[]},"correlation":false,"graph":false,"verify":false,"resolve":false}} +{"type":"handshake_ack","protocol_version":"contextgraph/1.0","provider":{"name":"example-docs","version":"1.0.0","data_flow":{"reads":true,"writes":true,"egress":true,"egress_scopes":["org-tenant","third-party-model","acme:vector-store"]}},"capabilities":{"query":{"kinds":["doc","snippet"]},"correlation":true,"graph":true,"embeddings_fingerprint":"bge-small-en-v1.5/384/l2","verify":true,"representations":["full","compact","reference"],"resolve":true}} {"type":"query","query":{"goal":"conformance probe: return your most relevant frames","query_text":"conformance probe","max_frames":8,"max_tokens":4096}} {"type":"query","id":"req-1","query":{"goal":"explain the retry policy","query_text":"retry backoff","embedding":[0.1,-0.25,0.5],"kinds":["doc","snippet"],"anchors":["file:///repo/src/retry.rs"],"max_frames":8,"max_tokens":4096,"as_of":"2026-07-21T12:34:56Z","representation_preferences":["compact","full"]}} {"type":"frames","result":{"frames":[],"truncated":false}} diff --git a/schema/validate-examples.py b/schema/validate-examples.py index c0713a2..6ab0848 100755 --- a/schema/validate-examples.py +++ b/schema/validate-examples.py @@ -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. @@ -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 diff --git a/sdk/create-contextgraph-provider/index.js b/sdk/create-contextgraph-provider/index.js index 5b7734e..78f1a47 100644 --- a/sdk/create-contextgraph-provider/index.js +++ b/sdk/create-contextgraph-provider/index.js @@ -31,7 +31,7 @@ const HERE = dirname(fileURLToPath(import.meta.url)); /** The published dependency each template resolves by default (see #59). */ const DEFAULT_SDK = { typescript: "^0.1.0", - python: "contextgraph-sdk>=0.1.0", + python: "contextgraph-sdk>=1.0.0", }; function fail(message) { diff --git a/sdk/create-contextgraph-provider/package.json b/sdk/create-contextgraph-provider/package.json index 8b33d8f..ebfb1dc 100644 --- a/sdk/create-contextgraph-provider/package.json +++ b/sdk/create-contextgraph-provider/package.json @@ -1,6 +1,6 @@ { "name": "create-contextgraph-provider", - "version": "0.1.0", + "version": "1.0.0", "description": "Scaffold a conformant Context Graph Protocol provider (TypeScript or Python) with contextgraph-inspect running in its CI from day one.", "license": "MIT OR Apache-2.0", "type": "module", diff --git a/sdk/go/contextgraph/types.go b/sdk/go/contextgraph/types.go index c7e3d46..fa1da1a 100644 --- a/sdk/go/contextgraph/types.go +++ b/sdk/go/contextgraph/types.go @@ -5,7 +5,7 @@ package contextgraph // ProtocolVersion is the protocol version this SDK speaks. -const ProtocolVersion = "contextgraph/1.0-draft" +const ProtocolVersion = "contextgraph/1.0" // Provenance is one link in a frame's provenance chain. Type is the wire name // for the entry's kind ("file", "derivation", ...). diff --git a/sdk/python/contextgraph_sdk/types.py b/sdk/python/contextgraph_sdk/types.py index ad1b83f..3b2663b 100644 --- a/sdk/python/contextgraph_sdk/types.py +++ b/sdk/python/contextgraph_sdk/types.py @@ -10,7 +10,7 @@ from typing import Any, Literal, TypedDict -PROTOCOL_VERSION = "contextgraph/1.0-draft" +PROTOCOL_VERSION = "contextgraph/1.0" FrameKind = Literal["snippet", "symbol", "fact", "doc", "memory", "episode", "graph"] Representation = Literal["full", "compact", "reference"] diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml index 47859ef..0fc227e 100644 --- a/sdk/python/pyproject.toml +++ b/sdk/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "contextgraph-sdk" -version = "0.1.0" +version = "1.0.0" description = "Zero-dependency Python SDK for building conformant Context Graph Protocol providers." readme = "README.md" requires-python = ">=3.9" diff --git a/sdk/typescript/package-lock.json b/sdk/typescript/package-lock.json index 375e2af..5f02e5c 100644 --- a/sdk/typescript/package-lock.json +++ b/sdk/typescript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contextgraphprotocol/typescript-sdk", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contextgraphprotocol/typescript-sdk", - "version": "0.1.0", + "version": "1.0.0", "license": "MIT OR Apache-2.0", "devDependencies": { "@types/node": "^22.0.0", diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index 9630ea6..822c2c9 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@contextgraphprotocol/typescript-sdk", - "version": "0.1.0", + "version": "1.0.0", "description": "Zero-dependency TypeScript SDK for building conformant Context Graph Protocol providers.", "license": "MIT OR Apache-2.0", "type": "module", diff --git a/sdk/typescript/src/types.ts b/sdk/typescript/src/types.ts index 3b40b37..2ba855a 100644 --- a/sdk/typescript/src/types.ts +++ b/sdk/typescript/src/types.ts @@ -8,7 +8,7 @@ */ /** The protocol version this SDK speaks. */ -export const PROTOCOL_VERSION = "contextgraph/1.0-draft" as const; +export const PROTOCOL_VERSION = "contextgraph/1.0" as const; /** What kind of thing a frame represents. */ export type FrameKind =