pyvolca: enforce + document + preflight engine wire compatibility#147
Merged
Conversation
pyvolca decodes a JSON wire whose shape changed with the activity_name/product_name rename, but nothing checked at runtime whether the engine actually speaks that wire — an older engine just produced an opaque decode error. Add volca._compat as the single source of truth (the wire this client requires + the engine-version hint) and record the engine's wireVersion on ServerVersion. Gate operations on it: the client checks once before its first call; Server.start() checks the engine it spawns and tears it down on a mismatch. get_version stays ungated so a bad engine is still inspectable, and VOLCA_SKIP_COMPAT_CHECK is a noisy opt-out. The README api-reference block is regenerated for the new ServerVersion field.
Add a Compatibility section to the README (so it shows on PyPI) and make the wire policy discoverable at install time: a static pyvolca <-> wire <-> engine history table plus the rule that wireVersion, not the version numbers, carries compatibility. The one code-derived line (which wire this build speaks, which engine it needs) is generated from volca._compat into its own marker block so it can't drift from the policy. gen_api_md's splice is generalized to handle both the api-reference and compatibility blocks, and a mirror drift test keeps the committed block in sync in CI.
Add scripts/release_precheck.py — a local gate answering "can I release pyvolca now?" before the tag is pushed (pyvolca-release.yml only checks tag==version, and only after the push). It verifies the version is new (vs the tag and PyPI), the changelog records it, the tree is clean, and — the point — that the engine release this pyvolca's wire requires (volca._compat.MIN_ENGINE_HINT) is already tagged, so a release can't strand users on a wire no published engine speaks. Green prints the exact tag commands; --no-tests skips the pytest+build leg.
refresh_stubs is the documented "engine was upgraded" path — the likeliest place to meet a wire mismatch — yet it fetched and parsed the OpenAPI spec without the one-shot gate _load_operations runs, so an incompatible engine slipped through silently. Run _ensure_compatible first; it refuses before the spec it couldn't decode is fetched. README api-reference block regenerated.
check_newer_than_pypi fell through to PASS whenever PyPI's latest didn't parse as plain semver (a pre-release suffix makes _semver return None), asserting "newer" on a comparison it never actually made. Return WARN when either side is unparseable.
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
pyvolca decodes a wire whose shape changed with the
activity_name/product_namerename, but no published engine emits that wire yet (last release v0.7.0).pip install pyvolca+Server.download()(which resolves the latest release) was therefore broken-by-default, with no version hint.What
Compatibility is now carried by the engine's
wireVersion(see the companion engine PR), not by version numbers — pyvolca and the engine version independently.volca._compatis the single source of truth (the wire this client speaks + the minimum engine it needs). The client checks once before its first call;Server.start()checks the engine it spawns and tears it down on a mismatch.get_versionstays ungated so a bad engine is still inspectable, andVOLCA_SKIP_COMPAT_CHECKis a noisy opt-out.## Compatibilitysection (visible on PyPI): a static pyvolca↔wire↔engine history table plus the policy. The one code-derived line is generated fromvolca._compat, with a drift test keeping it in sync.scripts/release_precheck.pyanswers "can I release pyvolca now?" locally: the version is new, the changelog records it, the tree is clean, and — the point — the engine release this pyvolca's wire requires is already tagged.pyvolca stays 0.6.0: the wire policy carries compatibility, not the version number.
Verified: the test suite passes; against a live engine emitting
wireVersion=1a client is accepted, while a client requiring a newer wire is refused with a clear error; the preflight correctly reports NOT READY until an engine ≥ v0.8.0 is tagged.