Skip to content

fix(sdk): serve verifiable file provenance from the three example providers - #71

Merged
macanderson merged 3 commits into
mainfrom
fix/sdk-example-file-provenance
Aug 1, 2026
Merged

fix(sdk): serve verifiable file provenance from the three example providers#71
macanderson merged 3 commits into
mainfrom
fix/sdk-example-file-provenance

Conversation

@macanderson

@macanderson macanderson commented Aug 1, 2026

Copy link
Copy Markdown
Owner

What

Main's CI is red on the three sdk … is a conformant implementation jobs (python, typescript, go). The new provenance-fixture-consistency check re-reads the bytes every file provenance names; the SDK examples cited file:///docs/… paths that exist on no machine with placeholder digests (sha256:1111…), so the check had nothing locally readable to verify and skipped — and conformance-external.sh requires every check green, none skipped.

How

Mirror the Rust reference provider's approach in each SDK example:

  • Ship the same two fixture files (getting-started.md, configuration.md) under each example's fixtures/ directory.
  • Resolve them to absolute file:// URIs from the example's own location (__file__ / import.meta.url / runtime.Caller(0)), cwd-independent.
  • Compute the real sha256 over the on-disk bytes at startup and serve it as both content_digest and the provenance digest — the digest a host re-derives when it re-reads the file (§6.2, §F5), so it can never drift from the bytes the URI names.

Ranges (L1-40/L1-25) clamp to EOF in the host's verifier, addressing the whole file — matching the whole-file digest, exactly as the Rust example does.

Verification

Ran .github/scripts/conformance-external.sh locally against all three fixed examples — all 13 checks pass for each, including provenance-fixture-consistency ("re-read and re-hashed 2 file-provenance digest(s) against the bytes on disk — all match").

Summary by Sourcery

Make the SDK example providers serve real, verifiable file-backed documentation so conformance provenance checks pass across languages.

New Features:

  • Add shared getting-started and configuration markdown fixtures to the Go, Python, and TypeScript example-docs providers.

Bug Fixes:

  • Update the Go, Python, and TypeScript example-docs providers to compute sha256 digests from on-disk fixture files and expose absolute file:// URIs, restoring provenance-fixture-consistency and conformance CI jobs.

Documentation:

  • Document the fix in the changelog, explaining how SDK example providers now expose verifiable file provenance for conformance.

…viders

The Python, TypeScript, and Go example-docs fixtures cited file:///docs/…
paths that exist on no machine, with placeholder digests (sha256:1111…).
provenance-fixture-consistency re-reads the bytes every file provenance
names; with nothing locally readable it skips, and conformance-external.sh
requires every check green, none skipped — so the three 'sdk is a
conformant implementation' CI jobs went red on main.

Each example now ships the same two fixture files the Rust reference
provider uses, resolves absolute file:// URIs from its own location, and
computes the real sha256 over the on-disk bytes at startup — the digest a
host re-derives when it re-reads the file (§6.2, §F5).

Verified locally: all 13 checks pass for all three providers via
.github/scripts/conformance-external.sh.
@sourcery-ai

sourcery-ai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR makes the Python, TypeScript, and Go SDK example-docs providers serve real, verifiable file provenance by shipping shared fixture markdown files, resolving them to absolute file:// URIs, and computing true sha256 digests from on-disk bytes, aligning the examples with the Rust reference and unbreaking conformance CI.

File-Level Changes

Change Details Files
Make the Go example-docs provider compute real sha256 digests from on-disk fixtures and use absolute file:// URIs for file provenance.
  • Introduce helper functions to resolve the fixtures directory from the source file path, build absolute file:// URIs, and compute sha256 digests over fixture file bytes.
  • Replace hard-coded placeholder digests with digests computed at startup from getting-started.md and configuration.md.
  • Update frame construction to use the new absolute URIs for both content URI and file provenance URI.
sdk/go/examples/example-docs/main.go
Make the TypeScript example-docs provider compute real sha256 digests from on-disk fixtures and use absolute file:// URIs for file provenance, handling both source and dist layouts.
  • Add filesystem, path, URL, and crypto imports and compute the fixture directory based on the module location, with a fallback that works from both built and source trees.
  • Introduce helper functions to build absolute file:// URIs and compute sha256 digests over fixture file bytes, tolerating missing files by hashing empty content.
  • Replace hard-coded placeholder digests with digests computed at startup from getting-started.md and configuration.md, and update frame construction to use the new absolute URIs for both content and provenance.
sdk/typescript/examples/example-docs.ts
Make the Python example-docs provider compute real sha256 digests from on-disk fixtures and use absolute file:// URIs for file provenance.
  • Add hashlib import and define FIXTURE_DIR relative to the example module so digest computation is cwd-independent.
  • Introduce helper functions to build absolute file:// URIs and compute sha256 digests over fixture file bytes, tolerating missing files by hashing empty content.
  • Replace hard-coded placeholder digests with digests computed at startup from getting-started.md and configuration.md, and update frame construction to use the new absolute URIs for both content and provenance.
sdk/python/examples/example_docs.py
Document the fix to SDK example providers and why it was needed in the changelog.
  • Add a Fixed entry explaining that the three SDK example providers now ship backing files, resolve absolute file URIs, and compute real sha256 digests so provenance-fixture-consistency can verify them and CI passes.
CHANGELOG.md
Add shared markdown fixture files for all three SDK example-docs providers so they have on-disk content to hash and serve.
  • Create getting-started.md and configuration.md fixtures for the Go example-docs provider.
  • Create getting-started.md and configuration.md fixtures for the Python example-docs provider.
  • Create getting-started.md and configuration.md fixtures for the TypeScript example-docs provider.
sdk/go/examples/example-docs/fixtures/configuration.md
sdk/go/examples/example-docs/fixtures/getting-started.md
sdk/python/examples/fixtures/configuration.md
sdk/python/examples/fixtures/getting-started.md
sdk/typescript/examples/fixtures/configuration.md
sdk/typescript/examples/fixtures/getting-started.md

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

@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.

Hey - I've found 1 issue, and left some high level feedback:

  • In all three SDKs, the fixture digest functions silently fall back to hashing empty bytes when the backing file is missing; consider failing fast (e.g., explicit error or process exit) so broken fixtures don't produce misleading but valid digests.
  • The Python _fixture_uri uses os.path.join, which will introduce backslashes on Windows; if cross-platform usage is expected, normalize to POSIX-style paths (e.g., pathlib.Path(...).as_posix()) before building the file:// URI.
  • In the TypeScript example, the FIXTURE_DIR resolution has a fallback to a non-existent fixtures folder under dist/examples when neither candidate exists; consider throwing an error if no candidate directory is found instead of silently using a likely-missing path.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In all three SDKs, the fixture digest functions silently fall back to hashing empty bytes when the backing file is missing; consider failing fast (e.g., explicit error or process exit) so broken fixtures don't produce misleading but valid digests.
- The Python `_fixture_uri` uses `os.path.join`, which will introduce backslashes on Windows; if cross-platform usage is expected, normalize to POSIX-style paths (e.g., `pathlib.Path(...).as_posix()`) before building the `file://` URI.
- In the TypeScript example, the FIXTURE_DIR resolution has a fallback to a non-existent `fixtures` folder under `dist/examples` when neither candidate exists; consider throwing an error if no candidate directory is found instead of silently using a likely-missing path.

## Individual Comments

### Comment 1
<location path="sdk/python/examples/example_docs.py" line_range="38-43" />
<code_context>
+FIXTURE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures")
+
+
+def _fixture_uri(file: str) -> str:
+    """The absolute ``file://`` URI a host re-reads to verify a frame's
+    provenance digest (``provenance-fixture-consistency``). Absolute and
+    cwd-independent, so verification never depends on the host's working
+    directory."""
+    return "file://" + os.path.join(FIXTURE_DIR, file)
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Normalize fixture URIs to valid `file://` URLs across platforms.

On POSIX this yields valid `file:///...` URLs, but on Windows `os.path.join` produces backslashes and `file://C:\...`, which is not a valid file URI and may fail when round-tripped or consumed by RFC-compliant clients. Consider using `pathlib.Path(FIXTURE_DIR, file).absolute().as_uri()` (or normalizing separators and enforcing a `file:///` prefix) so provenance URIs are portable and aligned with the TS/Go implementations.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread sdk/python/examples/example_docs.py
- CHANGELOG: contextgraph-types/host/conformance 0.1.0 and
  contextgraph-sdk 0.1.0 are live, each verified against the real
  registry (external scratch crate compiles; installed PyPI package
  passes all 13 conformance checks).
- sdk/PUBLISHING.md: flip the PyPI row to published; note the
  post-publish smoke copy needs the example's fixtures/ directory now
  that provenance digests are computed from real files.
- README: drop the 'badges read not-found until published' callout.
- sdk/python/README: drop the 'not yet published' install caveat.
@macanderson
macanderson merged commit 4377bff into main Aug 1, 2026
18 checks passed
@macanderson
macanderson deleted the fix/sdk-example-file-provenance branch August 1, 2026 01:01
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