Skip to content

feat(conformance): certify a downstream host's composition, and export the C7 rule - #70

Merged
macanderson merged 1 commit into
mainfrom
feat/host-selfcert-and-c7-preflight
Aug 1, 2026
Merged

feat(conformance): certify a downstream host's composition, and export the C7 rule#70
macanderson merged 1 commit into
mainfrom
feat/host-selfcert-and-c7-preflight

Conversation

@macanderson

@macanderson macanderson commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Why

Found while evaluating stella's conformance against this repo. All three gaps are
in the same place: the seam between the protocol and a host that isn't this
repository.

1. A downstream host had no way to be certified

run_host_conformance takes no arguments. It drives contextgraph_host::Host
and certifies the reference host — the only host here. But Host::query_all is
not the whole host: it audits budget honesty per provider and hands back a
fan-out. Something above it turns N providers' accepted frames into the one frame
set that reaches a prompt, and that step makes its own decisions.

Nothing checked that step, and the gap is not theoretical:

Three providers each returning one honest 400-token frame against a 1000-token
query are individually conformant — no token_cost lie, no frame flood — and
accepted_frames yields all three, for 1200 tokens.

A host that composed them as-is would pass every check in both existing suites.

Adds composition_conformance: a ComposingHost trait — given these providers
and this query, what reached the prompt and what did you drop getting there?

and four checks:

Check Rule
composition-budget-bound §7 — the admitted set fits max_tokens, including when only the cross-provider sum overflows
composition-total-partition issue #15 — every offered frame is admitted or reported dropped, never silently truncated
composition-quarantine §7 B2/B4 — a provider the audit rejected contributes nothing
composition-determinism context-reuse §1 — an unchanged frame set composes to the same render order

ReferenceComposingHost (query_all + compose_for_prompt) passes it, which is
what makes the bar satisfiable rather than aspirational. Each check also has a
purpose-built saboteur in the unit tests — correct in every respect but one — so
green is evidence. A suite whose red is unreachable is decoration.

Two bugs the first draft had, kept in the comments

Both are the kind of mistake the next person writing a check here will make:

  • The fixtures were B3 violators. They declared a token_cost their one-byte
    bodies did not justify. The reference host packs by canonical cost, so it
    measured 1 where the suite measured 400 — and the suite failed a correct
    host over a fixture defect. Every frame the suite offers now satisfies B3.
  • The quarantine saboteur passed for the wrong reason. It used a token_cost
    liar, whose frames are also over the token budget, so a host that skipped the
    audit entirely still dropped them while packing. It now uses a frame flooder,
    whose frames are individually cheap: only having consulted the audit keeps them
    out, which makes the check load-bearing instead of incidental.

2. The C7 loopback rule was private

refuse_insecure_transport is now pub. add_http already calls it, so C7 holds
either way; exporting it lets a host classify a URL before connecting and report
a plaintext endpoint as the configuration error it is, rather than as a connection
failure or a non-conformant provider.

The alternative is that every host re-derives "which hosts are loopback" locally
and C7 acquires one implementation per host, free to disagree about [::1],
127.0.0.2, or the casing of LOCALHOST. A normative rule with N
implementations is N rules.
The newly-public contract is pinned by a test over
exactly those cases, including the 127.0.0.1.example.com prefix trap.

stella consumes it in macanderson/stella#902.

3. The canary's warning was invisible

On 2026-07-29 the downstream canary caught a real break — stella's add_http call
had not been updated for the C7/C8 credential parameter — emitted its
::warning::, wrote its step summary, and reported the run as success,
because it is continue-on-error on every event. Nobody saw it. stella stayed
thirteen commits behind until a human went looking.

A warning on a green run is not a signal; it is a note in a file nobody opens. The
scheduled run now fails on a downstream break: it gates no PR and blocks no
merge, so failing costs nothing and buys a red run plus GitHub's
scheduled-failure notification. The pull_request path is untouched — a
downstream repo still cannot block a merge here, which was the right call and is
preserved.

Notes

  • Additive only. No wire type, envelope, or PROTOCOL_VERSION change; nothing
    published or tagged.
  • SPEC.md §11.1 records the composition harness alongside the host harness, so
    the enforcement inventory stays honest about what is and isn't checked — the
    suite's own residual (it cannot verify a reason is truthful, only that a drop
    is reported) is written down rather than implied.
  • Workspace green: clippy -D warnings, cargo fmt --check, full test suite.

Summary by Sourcery

Add a downstream host composition conformance suite and expose the C7 HTTP loopback rule as public API, while tightening the downstream canary workflow’s signaling on scheduled runs.

New Features:

  • Introduce a composition conformance module and trait for certifying downstream hosts’ frame composition behavior, including a reference implementation and public exports.
  • Add public access to the HTTP insecure transport (C7 loopback) rule so hosts can classify URLs before connecting, with tests covering loopback edge cases.

Enhancements:

  • Document the new composition harness and its checked rules in SPEC.md §11.1, alongside the existing host harness.
  • Expose internal host-conformance probe fixtures for reuse by the composition suite.

CI:

  • Adjust the downstream canary GitHub workflow so scheduled runs fail on downstream breaks while pull request runs remain advisory.

Tests:

  • Add unit tests and an external suite test ensuring the composition conformance checks both pass for the reference implementation and fail for targeted saboteur hosts.
  • Add tests pinning the exported C7 loopback classification behavior, including IPv4/IPv6 and loopback prefix traps.

…t the C7 rule

Three gaps found while evaluating stella's conformance against this repo, all of
them in the seam between the protocol and a host that is not this repo's.

## 1. A downstream host had no way to be certified

`run_host_conformance` takes no arguments: it drives `contextgraph_host::Host`
and certifies the reference host, which is the only host in this repository.
But `Host::query_all` is not the whole host. It audits budget honesty **per
provider** and hands back a fan-out; something above it has to turn N providers'
accepted frames into the one frame set that reaches a prompt, and that step makes
its own decisions.

Nothing checked that step, and the gap is not theoretical. Three providers each
returning one honest 400-token frame against a 1000-token query are individually
conformant — no `token_cost` lie, no frame flood — and `accepted_frames` yields
all three, for 1200 tokens. A host that composed them as-is would pass every
check in both existing suites.

Adds `composition_conformance`: a `ComposingHost` trait (given these providers
and this query, what reached the prompt and what did you drop?) and four checks —
the cross-provider token bound (§7), the total partition (every offered frame
admitted or reported dropped, never silently truncated, issue #15), the
quarantine (§7 B2/B4), and determinism (an unchanged frame set composes to the
same render order — the prompt-cache guarantee).

`ReferenceComposingHost` (`query_all` + `compose_for_prompt`) passes it, which is
what makes the bar satisfiable rather than aspirational. Each check also has a
purpose-built saboteur in the unit tests, correct in every respect but one, so
green is evidence: a suite whose red is unreachable is decoration.

Two things the first draft got wrong, both instructive enough to keep in the
comments:

- The fixture frames declared a `token_cost` their one-byte bodies did not
  justify — B3 *violators*. The reference host packs by canonical cost, so it
  measured 1 where the suite measured 400, and the suite failed a correct host
  over a fixture defect. Every frame the suite offers now satisfies B3.
- The quarantine check used a `token_cost` liar, whose frames are also over the
  token budget — so a host that skipped the audit entirely still dropped them
  while packing, and passed for the wrong reason. It now uses a frame flooder,
  whose frames are individually cheap: only having consulted the audit keeps
  them out.

## 2. The C7 loopback rule was private

`refuse_insecure_transport` is now public. `add_http` already calls it, so C7
holds either way; exporting it lets a host classify a plaintext URL *before*
connecting, and report it as the config error it is.

The alternative is that every host re-derives "which hosts are loopback"
locally and C7 acquires one implementation per host, free to disagree about
`[::1]`, `127.0.0.2`, or the casing of `LOCALHOST`. A normative rule with N
implementations is N rules. The newly-public contract is pinned by a test over
exactly those cases, including the `127.0.0.1.example.com` prefix trap.

## 3. The canary's warning was invisible

On 2026-07-29 the downstream canary caught a real break — stella's `add_http`
call had not been updated for the C7/C8 credential parameter — emitted its
`::warning::`, wrote its step summary, and reported the run as **success**,
because it is `continue-on-error` on every event. Nobody saw it; stella stayed
thirteen commits behind until a human went looking.

A warning on a green run is not a signal. The scheduled run now fails on a
downstream break: it gates no PR and blocks no merge, so failing costs nothing
and buys a red run plus GitHub's scheduled-failure notification. The
`pull_request` path is untouched — a downstream repo still cannot block a merge
here.

Workspace green: clippy `-D warnings`, `cargo fmt --check`, full test suite.
@sourcery-ai

sourcery-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a downstream host composition conformance suite, exports the C7 insecure transport rule for reuse by other hosts, and strengthens the downstream canary workflow’s signaling semantics while updating SPEC documentation and internal fixtures/utilities to support the new checks.

Sequence diagram for ReferenceComposingHost composition flow

sequenceDiagram
    participant TestSuite
    participant ReferenceComposingHost
    participant Host
    participant ContextProvider
    participant FanOut
    participant compose_for_prompt

    TestSuite->>ReferenceComposingHost: compose(providers, query)
    ReferenceComposingHost->>Host: new
    loop register providers
        ReferenceComposingHost->>Host: register(provider)
    end
    ReferenceComposingHost->>Host: query_all(query)
    Host-->>ReferenceComposingHost: FanOut
    ReferenceComposingHost->>ReferenceComposingHost: collect offered frames from FanOut.outcomes
    ReferenceComposingHost->>compose_for_prompt: compose_for_prompt(offered, query.max_tokens)
    compose_for_prompt-->>ReferenceComposingHost: composed (audit, included, excluded)
    ReferenceComposingHost->>ReferenceComposingHost: build admitted from included
    ReferenceComposingHost->>ReferenceComposingHost: build dropped as ExcludedFrame from excluded
    ReferenceComposingHost-->>TestSuite: Composition{admitted, dropped}
Loading

File-Level Changes

Change Details Files
Expose the C7 insecure transport rule as public API and pin its behavior with tests.
  • Change refuse_insecure_transport from private to pub and re-export it from the host crate root.
  • Add rustdoc explaining why the C7 rule is exported and how hosts should use it.
  • Introduce a unit test that exercises all loopback spellings, remote plaintext URLs, and invalid URL parsing to define the public contract.
contextgraph-host/src/http.rs
contextgraph-host/src/lib.rs
Make the downstream canary workflow fail on scheduled breaks while remaining advisory on pull requests.
  • Document the advisory vs gating behavior in the workflow header comments.
  • Adjust job naming to clarify behavior for PR vs schedule runs.
  • Change the flagging step to conditionally exit 0 for pull_request events and 1 for scheduled/manual events so breaks are visible via red runs and notifications.
.github/workflows/downstream-canary.yml
Document the new composition harness and its checks in the specification.
  • Add SPEC.md text describing the composition_conformance harness, ComposingHost concept, and the four composition checks (budget bound, total partition, quarantine, determinism).
  • Clarify how cross-provider budget overflow and drop reporting are handled and what remains unchecked.
SPEC.md
Introduce a composition conformance module that defines the ComposingHost trait, composition checks, and a reference implementation.
  • Add the composition_conformance module with ComposingHost trait, Composition/ExcludedFrame structures, and four named checks with adversarial fixtures.
  • Implement run_composition_conformance to drive all checks against an arbitrary composing host.
  • Provide ReferenceComposingHost that uses Host::query_all and compose_for_prompt to satisfy the suite, and helper functions/fixtures for honest frames and multi-provider setups.
  • Re-export composition_conformance types and constants from the crate root for downstream usage.
contextgraph-conformance/src/lib.rs
contextgraph-conformance/src/composition_conformance.rs
Expose internal host-conformance fixtures and ProbeProvider for reuse by composition_conformance.
  • Relax visibility of probe_query, frame, ProbeProvider, and local_flow from private to pub(crate).
  • Preserve behavior of host_conformance while enabling in-crate reuse of these utilities by the new composition suite.
contextgraph-conformance/src/host_conformance.rs
Add targeted tests ensuring the composition suite both passes for the reference host and fails for specific saboteur implementations.
  • Create unit tests for composition_conformance that verify ReferenceComposingHost passes all checks and that purpose-built saboteur ComposingHost implementations each fail exactly one rule.
  • Add an integration-style test that runs the public run_composition_conformance API against ReferenceComposingHost, asserting all four checks are present and pass and that the target label is propagated.
contextgraph-conformance/src/composition_conformance/tests.rs
contextgraph-conformance/tests/composition_conformance_suite.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

macanderson added a commit to macanderson/stella that referenced this pull request Jul 30, 2026
…ion vocabulary (#953)

Follow-up to #902, which landed the pin bump and deliberately deferred
three
items. This closes all three, plus a staleness bug the deferral was
hiding.

> **Stacked on
[context-graph-protocol#70](macanderson/context-graph-protocol#70
> The `contextgraph-*` pin points at that PR's branch sha (`c5fb2fe`)
and must be
> moved to a `main` sha once it lands. Everything below depends on two
surfaces it
> adds.

## 1. `recall_via_host` is now certified, not asserted

CGP #70 adds a composition conformance suite for the step above
`Host::query_all` — the merge across providers that turns a fan-out into
the frame
set a prompt actually gets. That step is `recall_via_host`, which is
Stella's own
code, which is why no upstream suite was ever going to cover it.

The gap it closes is specific: `query_all` audits budget honesty **per
provider**.
Three providers each returning one honest 400-token frame against a
1000-token
query are each within budget and jointly 200 over. Whether the prompt
ends up over
budget, and whether anyone is told what was dropped to keep it under, is
entirely
Stella's call.

`recall_via_host` now implements `ComposingHost` and passes all four
checks:

- cross-provider token bound
- total partition — every offered frame admitted or reported dropped
- quarantine — an audit-rejected provider contributes nothing
- deterministic render order

It passes **unmodified**, which is the interesting part: these are
exactly the
properties #713 fixed when the merge had no drop report at all and a
budget-squeezed frame simply vanished. This is a guard against a
regression this
codebase has actually shipped.

## 2. C7 refusals are reported honestly

Before: a plaintext `http://` URL to a non-loopback provider surfaced as
`Admission::NonConformant` — Stella blaming a remote provider for one
character of
local config. It was reported that way because the conformance probe
connects
first, and fixing it needed CGP to export its loopback rule.

Now: admission pre-flights the endpoint before the probe and reports
`Admission::InsecureTransport`, naming the peer and the fix. Ordering
matters on
its own terms too — the conformance suite *sends sample queries*, so
probing a
plaintext peer would put those on the wire. `contextgraph-host` refuses
the
connection either way, so C7 was never breached, but now nothing tries.

The rule is **called, not copied**. "Which hosts are loopback" has edge
cases
(`[::1]`, all of `127.0.0.0/8`, `LOCALHOST` casing, the
`127.0.0.1.example.com`
prefix trap); a second copy in Stella would be a second answer. A host
may consult
a protocol rule; it should not keep its own.

Also: a URL the parser rejects is now `Misconfigured` instead of
surfacing later as
`Unreachable`.

## 3. Attribution — vocabulary aligned, frame-keying scoped

`stella_core::context_record::ContextUseKind` is `Selected | Rendered |
Cited` —
exactly CGP §14's three observations (A2), spelled identically. Now
gated, like the
`Representation` seam #902 added: if CGP renames an observation or adds
a fourth,
this fails instead of the two layers quietly describing the same turn in
two
languages.

Frame-keyed attribution (an `AttributionReport` reconcilable against the
usage
report, A3) is **not** here, and not because it was forgotten.
`is_reconcilable` needs every record to name a `FrameId`; the extraction
path reads
`context_blocks`, which has `content_digest` and `memory_id` but **no
provider
column**. Two thirds of the triple is recoverable and the third is not —
that is a
store migration, not a vocabulary alignment. Emitting a report now with
`cited`
structurally false (it is only observable after the model answers) would
be the
dead capability surface CGP's own ADR 0004 exists to prevent.

## A correction, and the guard that missed it

#902 claimed that dropping `served_frames` weakens UR1. **That was
wrong**, and the
code comment now says so: every `ContextFrameRef` on the same event
carries
`provider`, `id`, and `content_digest`, so a billed total *is* walkable
back to the
exact triples. The `Option`s are meaningful, not lazy — an
unmaterialized candidate
has no id, a digest-less frame is un-revalidatable by §1 and must be
re-queried —
and CGP's own `FrameId.content_digest` is optional for the same reason.

Chasing that down exposed a real bug. The vendored
`docs/context-reuse.md` had its
**pin** bumped without its **body** re-fetched, so it claimed a rev it
did not
match. Upstream had grown §3 (consent scopes and receipts) — 106 lines —
and this
copy did not have it, while `check-normative-home.sh` went green because
it
compares shas and never content. Re-vendored from the pinned rev, the
now-resolved
§3 caveat corrected, and the trap written into the header so the next
pin bump does
not repeat it.

## One unrelated fix, to unblock the gate

`env_files::tests::home_directory_is_never_a_project_scope` fails on
macOS on clean
`origin/main` — verified at the branch point — and blocks the pre-push
gate, so it
is fixed here rather than bypassed with `--no-verify`. Its own commit.

The behaviour is correct; the test violated its own precondition.
`find_base`
documents that `start` arrives already resolved (`getcwd`) and so
resolves only
`$HOME`; the test passed an unresolved `tempdir()`, which on macOS is
`/var/folders/…` against a canonicalized `/private/var/folders/…`.
Canonicalized,
exactly as its sibling three tests below already does for the same
reason.

## Gates

1778 tests green across stella-cli/context/graph/core/protocol; clippy
`-D warnings` workspace-wide; `cargo fmt --check`; check-file-size;
check-normative-home; check-doc-citations. Full pre-push gate green.

https://claude.ai/code/session_015x5exqHcz9HkSGFN9Kwhu3

## Summary by Sourcery

Certify host-side composition against the updated Context Graph
Protocol, tighten C7 transport security handling for external providers,
and align the attribution vocabulary and docs with the new protocol
revision.

New Features:
- Introduce a CGP composition conformance gate for `recall_via_host`,
ensuring the host-side merge across providers satisfies protocol
composition guarantees.
- Add an explicit `InsecureTransport` admission outcome for plaintext
non-loopback providers, with operator-facing guidance tied to CGP C7.
- Gate the ledger's context-use vocabulary against CGP §14 by asserting
that local observation names match the protocol's retrieval-attribution
observations.

Bug Fixes:
- Preflight external HTTP endpoints for transport security and URL
validity so C7 violations and unparseable URLs are reported as
configuration or security issues rather than generic conformance or
reachability failures.
- Fix the `home_directory_is_never_a_project_scope` test on macOS by
resolving the temporary directory path to match `find_base`'s
expectations.

Enhancements:
- Document that usage reporting still satisfies UR1 without the
`served_frames` field by relying on frame references already present in
context events.
- Clarify and document the relationship between Stella's
`ContextUseKind` and CGP's attribution model, including shape and keying
differences.

Build:
- Bump `contextgraph-*` dependencies to the new pinned protocol revision
across crates.

Documentation:
- Re-vendor `docs/context-reuse.md` from the updated CGP revision,
incorporating the new consent scopes and receipts section and tightening
the normative-home header guidance.
- Update other normative design docs to point at the new protocol
revision and clarify the intent of the doc-citation checker.

Tests:
- Add tests that C7 transport security refusals classify plaintext
remote providers and loopback exceptions correctly and treat unparseable
URLs as configuration errors.
- Add a composition conformance test for `recall_via_host` that asserts
all protocol composition checks run and pass.
- Add a test asserting that the ledger's serialized attribution
observation names and count match CGP's retrieval attribution
vocabulary.

---------

Co-authored-by: Stella Test <test@stella.local>
@macanderson
macanderson force-pushed the feat/host-selfcert-and-c7-preflight branch from c5fb2fe to 733388a Compare July 30, 2026 21:57
@macanderson
macanderson marked this pull request as ready for review August 1, 2026 01:00
@macanderson
macanderson merged commit b5b38f3 into main Aug 1, 2026
17 of 20 checks passed
@macanderson
macanderson deleted the feat/host-selfcert-and-c7-preflight branch August 1, 2026 01:00

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @macanderson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

macanderson added a commit that referenced this pull request Aug 1, 2026
…og, and backfill the gaps (#73)

CHANGELOG.md relied on every PR remembering to write its own [Unreleased]
entry. An audit of the 22 merges since v0.1.0 whose numbers never appear
in the file found 9 user-visible changes with no entry at all — including
the OCP -> CGP rename itself, the repo's biggest breaking change. The
Unreleased section had also accumulated duplicate category headings
(two '### Added', two '### Changed', two '### Fixed') from PRs appending
their own blocks.

Go-forward: .github/workflows/changelog.yml watches pushes to main. When
a merge lands without touching CHANGELOG.md, it drafts the missing
entries from the merge's actual diff (.github/scripts/changelog-ai.sh,
Vercel AI Gateway, degrade-open: no key or a failed call is a log line,
never a red check) and proposes them as a bot/changelog PR — a draft for
human review, never a direct push. The gap is measured from the last
commit that touched CHANGELOG.md, so merging the bot PR resets it, and
each run regenerates the whole current gap, so a superseded bot PR loses
nothing.

Backfill: entries for the rename (#1), frame identity/composition/usage
reports (#32), golden wire fixtures (#35), ADR 0007 (#61, #27),
composition conformance + the canary scheduled-run gate (#70) — written
from each PR's diff, in the file's existing voice. Three further audit
hits (#68, #69, #71) turned out to be already covered by newer entries
and were left alone. Duplicate category headings merged: one heading per
category, Keep-a-Changelog order.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant