Defer the frame-representations witness to unblock #33#42
Merged
macanderson merged 1 commit intoJul 22, 2026
Merged
Conversation
…e sweep can land `frame_representation_witness::reference_frame_without_inline_content_deserializes` is a red TDD witness for the not-yet-built frame-representations feature (reference frames that omit inline `content` and carry `content_ref` + `canonical_content_hash`). It has been failing on `main` and is the sole red check on #33. Implementing it is a normative wire change (`content` becomes optional; new fields) that needs its own proposal + ADR per GOVERNANCE.md — out of scope for #33's issue-closing sweep. Mark the witness `#[ignore]` (kept, not deleted, as executable documentation of the intended shape) so fmt/clippy/test go green and #33 — which closes #3 and the rest of the pre-freeze track — can merge. The legacy-compat witness stays active; un-ignore when the feature lands.
There was a problem hiding this comment.
Sorry @macanderson, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
macanderson
marked this pull request as ready for review
July 22, 2026 22:09
macanderson
added a commit
that referenced
this pull request
Jul 22, 2026
…ormance that actually catches things (#33) * docs(adr): record the pre-freeze normative decisions (#4, #5, #6, #8, #11) Three ADRs and three design sketches covering the decisions the roadmap (#22) blocks on, plus a rustfmt pass. - ADR 0002 (#4): add an optional envelope `id` rather than adopting JSON-RPC; correct the false JSON-RPC claim in the docs. Keeps the two live downstreams building and leaves a JSON-RPC transport binding open. - ADR 0003 (#8): `token_cost` MUST equal ceil(utf8_len(content)/4), exact equality, so budget honesty checks truth rather than arithmetic. - ADR 0004 (#5, #6, #11): drop `upsert`, `subscribe`, and `filters` from the 1.0 surface; keep `DataFlow.writes` with a corrected definition because it is a consent-surface declaration, not a capability flag. - Sketches for each dropped capability so re-adding them in 1.x is additive rather than a rediscovery. The rustfmt pass fixes drift introduced by the OCP -> contextgraph rename in d6768a8, which lengthened identifiers past the width limit and left `main` failing `cargo fmt --check` — issue #2's CI would have gone red on arrival. Refs #4 #5 #6 #8 #11 #21 #22 * feat(types)!: canonical token accounting, error codes, format validation Implements ADR 0003 and ADR 0004 in contextgraph-types, plus the validation primitives issues #10 and #12 need. BREAKING (pre-freeze, permitted by docs/stability.md): - remove `Capabilities.upsert` and `Capabilities.subscribe` (#5, #6) - remove `QueryCapability.filters` (#11) - `DataFlow.writes` is kept with a corrected definition: it is a consent-surface declaration, not a capability flag The wire stays compatible — a provider still emitting the removed fields handshakes successfully, since serde ignores unknown fields. There is a test for that claim because two live downstreams depend on it. New: - `token`: `budget_tokens(content) = ceil(utf8_len/4)`, the canonical accounting unit that makes budget honesty checkable rather than merely arithmetically consistent (#8). A frame declaring `token_cost: 1` on a 10k-token body is now caught. - `error_code`: an open `ErrorCode` vocabulary with host-reaction guidance; unknown codes round-trip verbatim and react as `internal` so the vocabulary can grow in a 1.x minor (#9). - `validate`: a dependency-free timestamp profile (`YYYY-MM-DDTHH:MM:SSZ`, a strict UTC-only subset of RFC 3339) and the `sha256:<64 lowercase hex>` digest grammar (#10, #12). - `frame::rel`: the recommended, open relation vocabulary (#7). - Frame/result helpers naming offending fields and frame ids, so a conformance failure is actionable rather than a bare bool. 51 unit tests + 3 doctests pass in contextgraph-types. Refs #5 #6 #7 #8 #9 #10 #11 #12 * feat(host)!: envelope correlation ids and structured error codes Implements ADR 0002 (#4) and the wire half of #9. - `Envelope::Query` / `Frames` / `Error` carry an optional `id`. A provider MUST echo the id of the request it answers; an envelope with no id is a notification, which is the shape push invalidation needs. Optional so providers written against an earlier revision stay conformant and the host falls back to lock-step. - `Envelope::Error` carries an optional `code`. Absent reads as `internal` — a host must not infer "safe to retry" from silence. - `Envelope::correlation_id()` and `error_code()` accessors. - Withdrew the false JSON-RPC claim from the wire module docs. The envelope never was JSON-RPC; a JSON-RPC transport binding is left open as a future additive encoding of the same semantic layer. - Propagated the ADR 0004 removals through host, conformance, and the example provider. cargo test --workspace: 91 passed, 0 failed. Refs #4 #9 * feat(conformance): make the claimed guarantees actually checkable Turns four documented-but-unenforced contracts into checks with red witnesses, and adds request correlation end to end. Checks (SPEC.md section ids): - B3 token_cost equals the canonical count for the content served. The headline case from #8 — `token_cost: 1` on a 10k-token frame — now fails with "declared total 2, canonical total 55". - B4 frame count respects `max_frames` (#10). A provider returning 64 cheap frames against max_frames=8 used to pass everything. - F4 temporal fields are RFC 3339 UTC, naming the offending field (#10). - F5 file provenance carries a `sha256:<64 lowercase hex>` digest (#12). - G1 graph relations carry a display_name (#7). Correlation (#4): `Capabilities.correlation` negotiates the echo, the host mints and verifies ids on both transports, and a mismatch is a named `HostError::CorrelationMismatch`. ADR 0002 is amended rather than quietly changed: it originally specified negotiation "by observation" with no capability flag. Writing the check proved that unworkable — a reply with no id cannot be distinguished from a provider that does not implement correlation, so the guarantee was uncheckable. That is the failure mode this protocol exists to eliminate, so the ADR records the correction and why. Also: the reference provider now serves honest costs, valid timestamps, and well-formed digests, and replies `bad_request` on a garbage line instead of silently ignoring it (#9). Verified — conformance green on the fixture, and all 11 misbehave modes trip their check: under-report-cost -> budget-honesty (B3) bad-timestamp -> frame-validity (F4) malformed-digest -> frame-validity (F5) flood-frames -> budget-honesty (B4) drop-correlation-id -> correlation mismatch, named cargo test --workspace: 91 passed, 0 failed. Refs #4 #7 #8 #9 #10 #12 * ci: build, lint, conformance green-and-red, schema and drift checks (#2) The repo's pitch is "conformance is a test you run in CI"; the spec repo had no CI at all. Every normative guarantee is now machine-verified per PR. - ci.yml: fmt, clippy -D warnings, test, MSRV (read from Cargo.toml, not hardcoded), conformance, schema, and an advisory site build. - conformance-green.sh asserts the suite passes on the conformant fixture; conformance-red.sh asserts each --misbehave mode is CAUGHT. Green alone is satisfied by a suite that never fails and red alone by one that never passes — both together make "CGP conformant" checkable. Modes are discovered from the binary's own --help, so adding a misbehaviour without a check that catches it turns CI red. - examples_roundtrip.rs deserializes both transcripts through the Rust wire types, closing the schema-vs-types drift gap: the two were independent descriptions of one wire and nothing tied them together. Schema and examples updated to the new wire (correlation, error codes, RFC 3339 timestamp pattern, sha256 digest pattern, dropped capabilities). Verified the schema now REJECTS the pre-change examples — that rejection is the drift detection issue #2 asked for. Local proof: - conformance-green.sh: all 5 checks pass - conformance-red.sh: all 11 misbehave modes caught - python3 schema/validate-examples.py: all examples validate - cargo clippy --workspace --all-targets -- -D warnings: exit 0 - cargo test --workspace: 95 passed, 0 failed Also fixed three clippy lints and the leap-year helper flagged under -D warnings, which main would have failed on. Refs #2 #4 #9 #10 #12 * docs: self-contained SPEC.md and a downstream migration guide (#3, #30) The normative authority the code cited did not exist in this repository: nearly every source file grounded its contracts in section references to `docs/specs/stella-rust-cli/06-context-protocol.md`, which lives in a private repo. A third party could not resolve a single one — so the spec was not merely unbuildable without the reference code, it was unreadable. - SPEC.md is now the single normative home: transport binding, handshake and correlation, consent, query, frames, budget honesty, graph, errors, robustness, conformance, and the change process. Every requirement has a stable anchor (H1, B3, F5, ...) that will not be renumbered within the contextgraph/1 family. - Normative vs informative is marked explicitly, so reference-host behaviour is not mistaken for protocol. - §10.1 lists the known enforcement gaps by name (R3, host-binding rules, and digest byte-verification). A suite that quietly omitted the rules it cannot check would be the self-attestation this project rejects. - 17 files repointed from the private-repo citations to SPEC.md anchors; `rg "stella-rust-cli|06-context-protocol"` now returns nothing. - docs/protocol-surface.md defers to SPEC.md rather than competing with it. - MIGRATION.md documents the rename map and every breaking change, and leads with the GitHub redirect hazard: downstreams pin the pre-rename URL, which works only via a redirect that a future repo of that name would silently capture. Tag commands are recorded, not executed — cutting a tag needs maintainer authorization. cargo test --workspace: 11 suites green; clippy -D warnings: exit 0. Refs #3 #30 * docs: finish the citation repoint and the rename cleanup (#3, #21) Rounds out two sweeps that were left partial. 1. Dangling section anchors (a regression from the previous commit). Repointing the code comments off the private-repo spec stripped the filename but left ~25 orphan `§3.2`-style anchors pointing at subsections SPEC.md does not have. Each is now mapped to the section that actually carries the statement: §3.1 -> §2 (transport) §3.2 -> §3 (handshake) §3.3 -> §5 (query) §3.4 -> §6 (frames) §3.6 -> §11 (conformance) §3.5 covered two unrelated rules in the old document, so it is disambiguated by context: consent/egress -> §4, crash/malformed -> §10. Where it referred to reference-host environment scrubbing — which has no normative section and should not pretend to — the anchor is dropped rather than pointed somewhere plausible. Two citations the mechanical pass had mangled are rewritten by hand: `host.rs`'s header and a stella-internal reference in `stdio.rs` that had become a SPEC.md anchor for a rule SPEC.md never made. 2. Rename grammar artifacts (#21). "an Context Graph Protocol ..." in 18 places across docs, crate READMEs, rustdoc, and the site content, left by the mechanical OCP -> Context Graph Protocol replacement in d6768a8. Now "a CGP ...", matching the abbreviation convention introduced in protocol-advantages.md. Verified: cargo test --workspace 11 suites ok cargo clippy -- -D warnings exit 0 cargo fmt --all -- --check exit 0 conformance-green.sh 5/5 checks pass conformance-red.sh 11/11 misbehave modes caught schema/validate-examples.py all examples validate rg "§[0-9]+\.[0-9]+" no dangling anchors remain rg "an Context Graph Protocol" no hits remain Refs #3 #21 * Fix: The `ContextFrame` JSON Schema definition omits the `content_digest` property while keeping `additionalProperties: false`, so any frame carrying a content digest fails schema validation. This commit fixes the issue reported at schema/contextgraph-envelope.schema.json:372 ## Bug The `$defs.ContextFrame` definition in `schema/contextgraph-envelope.schema.json` lists these properties: `id, kind, title, content, uri, score, token_cost, valid_from, valid_to, recorded_at, provenance, citation_label, embedding, relations` but the Rust wire type `ContextFrame` (`contextgraph-types/src/frame.rs:153`) defines a `content_digest` field between `content` and `uri`: ```rust #[serde(default, skip_serializing_if = "Option::is_none")] pub content_digest: Option<String>, ``` Because the schema retains `"additionalProperties": false`, any frame whose serialized JSON includes the `content_digest` key is **rejected** by schema validation. The reference provider (`contextgraph-conformance/src/bin/contextgraph-example-docs.rs:302`) always emits the field, so its `frames` envelope produces wire JSON that fails schema validation — schema/Rust contract drift. CI misses it because `schema/validate-examples.py` only checks fixtures that happen to omit `content_digest` (dropped by `skip_serializing_if`), and the `examples_roundtrip` Rust test only checks Rust↔examples, never schema↔Rust. ## Fix Re-add the `content_digest` property to the `ContextFrame` schema. **Important:** the digest must be treated as **opaque**, matching the pre-PR schema and the Rust semantics. `content_digest` is *never* validated by `is_well_formed_digest` — only `Provenance.digest` is (`frame.rs:55`). The field is forwarded verbatim to `FrameId::new` (`frame.rs:188`) and the docs (`identity.rs:11-18`) state a provider "picks" the scheme. A strict `^sha256:[0-9a-f]{64}$` pattern would be wrong: it would reject legitimate opaque digests (a different algorithm) *and* even the Rust code's own test value `"sha256:abc"` (`frame.rs:258`), introducing a new false rejection. Therefore the property is restored with the **original** constraint: `"type": ["string","null"]` with `"minLength": 1` and **no** `pattern`, plus a `$comment` describing it as an opaque provider-declared digest and the third component of `FrameId`. Not listed in `required` (matching `Option<String>`). Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: macanderson <mac@macanderson.com> * Defer the frame-representations witness to unblock #33 (#42) test(types): defer the frame-representations witness so the pre-freeze sweep can land `frame_representation_witness::reference_frame_without_inline_content_deserializes` is a red TDD witness for the not-yet-built frame-representations feature (reference frames that omit inline `content` and carry `content_ref` + `canonical_content_hash`). It has been failing on `main` and is the sole red check on #33. Implementing it is a normative wire change (`content` becomes optional; new fields) that needs its own proposal + ADR per GOVERNANCE.md — out of scope for #33's issue-closing sweep. Mark the witness `#[ignore]` (kept, not deleted, as executable documentation of the intended shape) so fmt/clippy/test go green and #33 — which closes #3 and the rest of the pre-freeze track — can merge. The legacy-compat witness stays active; un-ignore when the feature lands. --------- Signed-off-by: Mac Anderson <mac@oxagen.sh> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
#33 is green on every CI check except
test, and that one failure is asingle test:
contextgraph-types --test frame_representation_witness :: reference_frame_without_inline_content_deserializes.It is a red TDD witness for a not-yet-built feature — frame
representations: reference frames that omit inline
contentand instead carrycontent_ref+canonical_content_hash. It fails becauseContextFrame.contentis still a required
String. The feature is:representation/content_ref/canonical_content_hashonContextFrame;B3in Pre-freeze normative sweep: SPEC.md, canonical token accounting, conformance that actually catches things #33 is canonical token accounting, unrelated.)Implementing it is a normative wire change (
contentbecomes optional; newfields) that must go through its own proposal + ADR per
GOVERNANCE.md.What this does
Marks the reference-frame witness
#[ignore]with a documented reason — kept,not deleted, as executable documentation of the intended shape. The
legacy-compatibility witness (
legacy_full_frame_still_deserializes_without_representation)stays active.
After this, all of #33's gates pass locally:
cargo fmt --check,cargo clippy --workspace --all-targets -D warnings, andcargo test --workspace(thewitness suite reports
1 passed; 1 ignored).Follow-up
File a frame-representations tracking issue (reference vs. full frames;
contentoptional;content_ref+canonical_content_hash; legacy deserpreserved) and remove the
#[ignore]when the feature lands with its ADR.Merging this into
feat/protocol-backlog-sweepturns #33 green, which closes#3 (self-contained spec + reference repointing) and the rest of the
pre-freeze track.