Skip to content

fix(genvm): support genvm-manager, and reuse the harness-built GenVM - #19

Merged
MuncleUscles merged 4 commits into
v0.11-devfrom
fix/genvm-manager-support
Jul 23, 2026
Merged

MuncleUscles merged 4 commits into
v0.11-devfrom
fix/genvm-manager-support

Conversation

@MuncleUscles

@MuncleUscles MuncleUscles commented Jul 23, 2026 •

Copy link
Copy Markdown
Member

Two commits. The first makes the linter work at all against GenVM v0.6; the second stops it downloading its own GenVM when one has already been built for it.

1. The linter was stranded on the retired genvm repo

Three independent breaks, each verified against source and against a real genvm-manager v0.6.0-rc1 bundle:

Bundle source hardcoded. Three URLs pointed at genlayerlabs/genvm, whose newest non-prerelease is v0.3.0-rc7. That bundle carries py-genlayer hashes 1jb45aa8…/1zr6nqk5… but not the v0.6 hash 9b8kjyda…, which is why Studio's lint job failed with filename 'runners/py-genlayer/9b/8kjyda….tar' not found. There was no way to override the repo — only the version — so this could not be fixed by configuration. Now a single GENVM_REPO constant (env-overridable) defaulting to genlayerlabs/genvm-manager. Both manager tags are GitHub prereleases (their names contain a hyphen), so prerelease handling and a reachable FALLBACK_VERSION came with it.

The SDK moved. from genlayer.py.get_schema import get_schema — the v0.3+ SDK has genlayer/_internal/get_schema.py and no genlayer.py package at all. Now tries the new path and falls back to the old, so both eras load. The guard inspects exc.name so a genuinely broken new SDK still raises instead of being masked.

Silent false-pass — the worst of the three. v0.6 contracts declare class Storage(gl.contract.Contract), a nested ast.Attribute. All three subclass detectors required base.value to be an ast.Name, so no v0.6 contract was ever recognised as a contract: rules E011–E022 and the contract-scoped safety rules were skipped entirely and genvm-lint lint reported clean while checking nothing. Now one shared lint/ast_utils.py helper used at all three sites, matching Contract, gl.Contract, gl.contract.Contract, genlayer.Contract, genlayer.contract.Contract.

Also: cached bundles are now namespaced per repository. Versions are not unique across repos and their contents differ, so an unqualified cache let an old-repo bundle satisfy a manager request — which would have silently defeated the repoint for every existing user.

2. It downloaded its own GenVM instead of using the one already built

The E2E harness builds GenVM once per run and exports GENVM_PREBUILT_DIR into the linter's subprocess. The linter never read that variable — only GENVMROOT. And the GENVMROOT path was itself dead against modern trees: it looked for unpacked runners/<type>/src directories, but a real GenVM root contains runners/<type>/<2>/<rest>.tar tarballs. So it silently fell through and fetched its own ~310MB bundle, validating contracts against a different GenVM than the one under test.

Adds explicit source modes, precedence prebuilt > release:

Mode Selected by
prebuilt GENVM_PREBUILT_DIR (or GENVMROOT, kept as an alias) pointing at a usable tree
release default — the existing download path, unchanged

GENVM_SOURCE_MODE selects explicitly. When prebuilt is declared and the tree is unusable, the build hard-fails with an actionable message rather than quietly downloading — that is what makes "the harness said prebuilt" verifiable. With the mode unset, behaviour is unchanged: use the tree if usable, else download.

Tree resolution handles both on-disk layouts (runners/…, falling back to executor/*/legacy-runners/…), so v0.5-era contracts still resolve from a manager tree. A candidate root is validated the way the harness validates it — bin/genvm-modules and runners/ — so a half-populated directory is rejected rather than half-used.

Verification

93 unit tests pass (86 before). Verified end-to-end against a real GenVM root and a real Studio contract pinned to 9b8kjyda…:

  • prebuilt mode → ✓ Validation passed with no download at all, exit 0
  • prebuilt declared + broken tree → ✗ Validation failed … missing bin/genvm-modules, exit 1, no download
  • The contract-detection test fails on pre-fix code, so it pins the false-pass bug rather than merely passing.

Publishing

publish.yml triggered only on push to main, but main auto-mirrors the active dev branch (v0.11-dev, version 0.10.0) while releases come from the stable branch (v0.11, version 0.11.0). A fix merged into v0.11 therefore never reached main and never published — and had it reached main, the 0.10.0 version there would have been skipped as already-published. Now triggers on the stable v[0-9]+.[0-9]+ branches. The already-published guard is kept; it was correct, just fed the wrong branch.

No version bump here — choosing the next version is a human call.

Companion

Pairs with genlayerlabs/genlayer-e2e#669, which makes the harness declare GENVM_SOURCE_MODE=prebuilt. Safe in either order: an older linter ignores the variable, and this linter without the harness change simply uses auto-detect.

Supersedes the detection half of #18 (which widens only one of the three sites, and still requires an ast.Name, so it does not match gl.contract.Contract). Left open for its author to close.


3. Nothing in CI ran the tests

There was no workflow running this repo's own test suite — only branch-policy, retarget, fast-forward and the cross-repo E2E gate. And the E2E scenario runs pytest tests/test_artifacts.py, whose every case monkeypatches urllib.request.urlopen / _download_to, so nothing is downloaded, extracted, or linted for real.

That is why all three breaks above shipped: no automated check on this repo could have caught them.

Adds a Tests workflow on Python 3.10 (the requires-python floor) and 3.12, so a version-specific regression cannot hide behind a single interpreter.

Adding it immediately exposed a live crash. stubs.py bound the whole (paths, notes) tuple returned by extract_sdk_paths — the only one of four call sites that did not unpack it:

sdk_paths = extract_sdk_paths(tarball_path, {})   # -> (paths, notes)
if not sdk_paths:                                  # a 2-tuple is always truthy: dead guard
    raise RuntimeError("Could not extract SDK paths")
sdk_path = sdk_paths[0]                            # -> the list, not a Path
src_path = sdk_path / "src" ...                    # TypeError: unsupported operand type(s) for /

So genvm-lint stubs raised TypeError every time, and the guard meant to catch "no paths" could never fire. Fixed, with a regression test verified to fail against the pre-fix code. This is the VS Code extension's stub-generation path.

Deliberately not in this PR

The repo declares select = ["E", "F", "I", "W"] in pyproject.toml but has never enforced it, so 26 violations have accumulated (19 E501, 7 I001) across files this branch does not touch. I left the lint step out rather than bury a GenVM change under unrelated line-wrapping, or land a red check. Worth its own cleanup PR — the config is already there, it just needs running.

The linter resolved bundles from the retired genvm repo, imported get_schema from a path the v0.3 SDK no longer has, and failed to recognise gl.contract.Contract so every v0.6 contract silently skipped all lint rules.
The E2E harness builds GenVM once and exports GENVM_PREBUILT_DIR, but the linter only read GENVMROOT and its lookup expected unpacked source dirs, so it downloaded its own bundle and validated against a different GenVM than the one under test.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 42f3a624-3d7e-4b2e-a132-77e50429a745

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/genvm-manager-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Nothing in CI ran the 93 unit tests, which is why three breaks shipped. Adding the job surfaced that stub generation binds the whole (paths, notes) tuple, so it always raised TypeError on the first path join.
The repo declares select=[E,F,I,W] but has never enforced it, so 26 violations have accumulated across files this branch does not touch. Enforcing it belongs in its own change.
@MuncleUscles
MuncleUscles merged commit e94398b into v0.11-dev Jul 23, 2026
11 of 12 checks passed
@MuncleUscles
MuncleUscles deleted the fix/genvm-manager-support branch July 23, 2026 22:31
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