Skip to content

CR-DIST-06: stop distribution manifests drifting from the shipped version - #247

Merged
harishquantamix merged 2 commits into
masterfrom
cr-dist-06-public
Aug 2, 2026
Merged

harishquantamix merged 2 commits into
masterfrom
cr-dist-06-public

Conversation

@quantamixsol

Copy link
Copy Markdown
Owner

CR-DIST-06 — stop distribution manifests drifting from the shipped version

Public port of private #328 (d2cbf28c), merged 2026-08-02. All six files are
byte-identical to private/master.

The problem, on this repo right now

Manifest public master PyPI
server.json 0.80.0 0.83.0
plugins/claude-code/.../plugin.json 0.80.0 0.83.0
plugins/codex/.../plugin.json 0.80.0 0.83.0

Three releases stale. Not a paywall leak — the plugins invoke graq from the user's
own environment and pin no version, so a stale manifest never installs old code. It is a
listing advertising a version we no longer ship, on the copy people actually browse.

The subtlety

server.json was only half broken: the registry workflow already rewrote it inside
the CI checkout
before publishing, so the published registry entry was always
correct
— the rewrite was simply never committed. The two plugin manifests were
touched by nothing at all, and those are read straight from GitHub.

Correct artifact, wrong repo. They now agree — which also stops the next person
investigating a registry bug that never existed.

What ships

  • scripts/ci/sync_manifest_versions.py — syncs all three. --check exits 1 on
    drift; strict PEP 440 (malformed version → exit 2); a missing key raises rather than
    silently no-opping. Output matches the stored format, so a sync is a one-line diff.
  • publish-mcp-registry.yml — calls the script instead of the inline
    server.json-only heredoc, and commits back after publish+verify.
  • Drift guard test asserting every manifest == graqle.__version__ — what would have
    caught this three releases ago.

Sentinel BLOCKED pass 1 — both blockers correct, both fixed

B1git checkout master fails on the default shallow checkout; the step would have
died with pathspec 'master' did not match any file(s) after a successful publish,
leaving manifests stale — manufacturing the very defect it fixes. Reproduced locally with
git clone --depth 1 --branch v0.83.0 --single-branch before fixing. → fetch-depth: 0.

B2git pull --ff-only hard-fails if anything lands on master mid-job. → 3-attempt
fetch/reset/sync/push loop; the sync is idempotent so replaying is safe.

After 3 failed attempts it warns rather than failing — the package is already
published and the registry entry already correct, and failing a green release over a
cosmetic string trains people to ignore red. The drift guard catches it next PR. Sentinel
reviewed that deviation explicitly and approved it.

Evidence

  • Mutation-tested — reverting server.json to 0.80.0 makes the guard fail.
  • 7 new tests pass on this branch · packaging + licensing 329 passed, 0 failed on private.
  • All 6 files byte-identical to private/master.
  • Sentinel pass 2: APPROVE, 0 blockers, 91%.

Rule #0

git remote -v run before push. Public cherry-pick authorised only because private
#328 is merged (2026-08-02T16:41:10, base advanced to b2a08e77).

🤖 Generated with Claude Code

…sion

Three manifests carried hand-maintained version strings that nothing updated.
They reached 0.80.0 while PyPI served 0.83.0 — three releases stale.

Not a paywall leak: the Claude Code / Codex plugins invoke `graq` from the
user's own environment and pin no version, so a stale manifest never installs
old code. It is a listing that advertises a version we no longer ship, which
reads as an abandoned project.

server.json was subtler. The registry workflow already rewrote it INSIDE the CI
checkout before publishing, so the published registry entry was always correct
— but the rewrite was never committed, so the file in git stayed stale forever.
Correct artifact, wrong repo. The two now agree.

The two plugin manifests were touched by nothing at all, and users read those
straight from the GitHub repo. Those were the genuinely broken ones.

- scripts/ci/sync_manifest_versions.py — sets the version across all three.
  --check reports drift and exits 1. Strict PEP440 (a malformed version must
  never reach a public listing). A missing key raises rather than silently
  no-opping; an absent file is reported, not failed. Output format matches how
  the files are stored, so a sync is a one-line diff, not a reformat.
- publish-mcp-registry.yml — calls the script instead of the inline
  server.json-only heredoc, and commits the result back to master AFTER the
  publish+verify steps. Committing a version we then failed to publish would
  advertise a release that does not exist.
- Drift guard test asserting every manifest == graqle.__version__ — this is
  what would have caught the 0.80.0 drift three releases ago.

Sentinel BLOCKED pass 1 on two workflow defects, both correct, both fixed:

  1. `git checkout master` fails on the default shallow single-branch checkout
     — no local master ref exists, so the step dies with "pathspec 'master' did
     not match any file(s)" AFTER a successful publish, leaving manifests stale.
     Reproduced locally with `git clone --depth 1 --branch v0.83.0
     --single-branch` before fixing. Fixed with fetch-depth: 0.
  2. `git pull --ff-only` hard-fails if anything lands on master mid-job. Now a
     3-attempt fetch/reset/sync/push loop; the sync is idempotent so replaying
     on newer master is safe.

After 3 failed attempts this warns rather than failing the job: the package is
already published and the registry entry already correct, so failing a green
release over a cosmetic listing would train people to ignore red. The drift
guard then catches it on the next PR instead of letting it rot for three
releases. Sentinel reviewed that deviation explicitly and approved it.

Mutation-tested: reverting server.json to 0.80.0 makes the drift guard fail;
restoring makes it pass. 7 new tests; packaging + licensing 329 passed 0 failed.
Sentinel pass 2: APPROVE, 0 blockers, 91%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

🛡️ GraQle PR Guardian

💥 Blast Radius: 8 modules affected

Module Files Changed Risk Level Impact Radius
.agents 1 🟡 T2 1
.claude-plugin 1 🟡 T2 1
.github 1 🟡 T2 1
plugins 2 🟡 T2 2
scripts 1 🟡 T2 1
server.json 1 🟡 T2 1
tests 1 🟢 T1 1

Total blast radius: 8


🏛️ Governance Verdict

⚠️ WARN

  • T2: Advisory warnings detected. Review recommended.

🔍 SHACL Violations

No SHACL violations detected.


🔐 Approval Requirements

This PR requires approval from: T2

  • A Senior Engineer or above must approve.

Approval requirement NOT yet satisfied.


Metric Value
Blast Radius 8
Files Analyzed 8
Blocked 0
SHACL Violations 0
Verdict WARN

🔬 Powered by GraQle PR Guardian v0.1.0 · Scan completed 2026-08-02T20:13:51.251148+00:00

Found while checking what a Claude Code directory reviewer actually fetches:
.claude-plugin/marketplace.json still advertised 0.80.0.

Root cause of the miss: the marketplace version is NESTED under plugins[0].
A top-level grep for '"version"' finds only metadata.version and walks straight
past it, which is how I concluded these files "carry no version" in the first pass.

- Both marketplace manifests added as sync targets (plugins.0.version).
- metadata.version deliberately NOT synced: it is the marketplace SCHEMA version
  (1.0.0), not the SDK release. Syncing it would corrupt the manifest. Verified
  it still reads 1.0.0 after a sync.
- ensure_ascii=False when writing: the marketplace description contains a real
  em-dash, and default json.dumps rewrote it as — — turning a one-line
  version bump into a mojibake diff on a file reviewers read.
- Workflow now `git add -A` instead of an explicit file list. The script owns
  which files are targets; an explicit list silently drops targets added later,
  which is precisely the failure this commit is fixing. Safe because the retry
  loop resets to origin/master first, so nothing else can be dirty.

9 tests (was 7). Diff is 2 version lines, no reformatting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@harishquantamix
harishquantamix merged commit 032a316 into master Aug 2, 2026
19 checks passed
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.

2 participants