Vendor packse scenarios and cross-check against a SAT oracle - #61
Merged
Merged
Conversation
… oracle Vendors astral-sh/packse's 147 non-example scenario fixtures under resolver/testdata/packse (pinned commit and refresh steps in its README), loads them with BurntSushi/toml, and runs the non-universal ones against resolver.Resolve. A second, independent CNF encoding built from the same scenario data and solved with crillab/gophersat cross-checks the resolver's own answer in both directions. 41 universal scenarios are out of scope (RFD 0001 defers universal resolution). 4 use no_build/no_binary, which resolver.Options has no knob for. 8 run and are asserted to still disagree with packse's expected result, each with a one-line reason (PEP 592's yanked exact-pin exception, an extras edge case, a pre-release fallback pip/uv apply that candidate.PrereleaseSet does not, and Requires-Python upper bounds, which uv ignores and go-pyresolver does not). The remaining 94 pass exactly. Refs rstudio/package-manager#18658 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Match exactPinAdmitsYanked's yanked exact-pin check on the parsed requirement name instead of a raw substring, closing a false-positive risk from a prefixed package name. Guard buildOracleModel's final conjunction against zero clauses (unreachable today, but the same gophersat empty-AND footgun as atMostOne). Drop an unused binding. Refs rstudio/package-manager#18658 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Vendors astral-sh/packse's scenario fixtures and runs them against
resolver.Resolve, cross-checked by an independent SAT oracle. Test-only; no production code changed.What this does
astral-sh/packseat commit18be7766b14aa3e03db37bc575959b3f4c6fbacb(2026-08-18, after tag0.3.59) intoresolver/testdata/packse/, plus itsLICENSE-APACHE/LICENSE-MIT. Seeresolver/testdata/packse/README.mdfor the exact copy command and how to refresh.resolver/packse_scenario_test.go: parses the scenarios withBurntSushi/toml(test-only import), failing on any undecoded key so a re-pull that adds a field can't silently drop it.resolver/packse_test.go: runs each non-universal scenario against the realresolver.Resolve, classified into three named lists (see below).resolver/satoracle_test.go: builds a CNF encoding of the same scenario data (never fromprovider/candidateoutput) and solves it withgithub.com/crillab/gophersat(test-only import), cross-checking the resolver's own answer in both directions.Summary line
Classification
outOfScope(41):resolver_options.universal = true(all 32 offork/, 5tag_and_markers/, 2backtracking/, 2wheels/). go-pyresolver resolves one concrete marker environment at a time; universal resolution is deferred by RFD 0001.unsupportedOption(4):wheels/no-binary,no-build,no-wheels-no-build,only-wheels-no-binary—resolver.Optionshas nono_build/no_binaryequivalent. The harness refuses to run these rather than silently ignoring the option.knownFail(8): run against the real resolver, asserted to still disagree with packse's expected result. Nevert.Skip— if one starts passing, the assertion flips to "unexpectedly passed, remove it from knownFail".extras/missing-extra,extras/extra-does-not-exist-backtrackname[extra]as a virtual package requiring a candidate that declares the extra; a candidate that omits it is excluded rather than the extra being silently dropped, which is what uv doesprereleases/package-only-prereleases,-boundary,transitive-package-only-prereleasescandidate.PrereleaseSetadmits a pre-release only via a specifier or an explicit opt-in; it does not implement pip/uv's further fallback of admitting one when a package publishes no final release at allrequires_python/python-less-than-currentyanked/transitive-package-only-yanked-in-range-opt-in,transitive-yanked-and-unyanked-dependency-opt-inFilteredIndex.ExcludeYankeddrops a yanked version outright; PEP 592 (and uv) still allow it when a requirement pins it exactlyNone of these are fixed in this PR (test-only, per the issue's scope). Each is a real, reproducible gap for a later PR to pick up.
Three-way disagreement table (packse expected / resolver / oracle)
The oracle implements the PEP 592 exact-pin exception directly (the resolver's known limitation above), so it agrees with packse and disagrees with the resolver on the two yanked scenarios. On the other 6, the oracle reuses the same admission input as the resolver (
candidate.PrereleaseSet, and the same strict extras model), so it agrees with the resolver and disagrees with packse — i.e. the oracle's disagreement count (7) is a subset of the resolver's knownFail (8), minus 2 yanked scenarios where it goes the other way:Shared pre-release admission input
Per the brief's one deliberate exception: the oracle takes its admissible pre-release version set from
candidate.PrereleaseSet(not fromprovider), plus Requires-Python and wheel/sdist availability it computes itself. Everything else in the CNF is built from the scenario's own TOML data.Mutation proofs (run locally, not committed)
candidate.Newest.Lessto oldest-first: 13 package-list scenarios went RED.requires_python/python-less-than-currentfromknownFail: RED (expected-fail scenario now reported as a real failure). Addedlocal/local-simpletoknownFail: RED ("unexpectedly passed, remove it from knownFail").undecoded keys [...]), RED.Consumer impact (PPM, not this PR)
PPM pins
go-pyresolverand vendors its dependency tree. Verified by experiment: a test-only import (satin this case) does not enter a consumer'sgo.modorvendor/tree undergo mod tidy/go mod vendor, but does appear ingo list -m allunder graph pruning. Expect at most agithub.com/crillab/gophersat v1.4.0/go.mod h1:line in PPM'sgo.sumthe next time it bumps this module — nothing changes in the vendored code, and module-graph license/SBOM scanners may list gophersat (MIT, harmless).Not in this PR
knownFailtable above) — each gets its own later PR with the scenario as the red test.NEWS / CHANGELOG
No PPM
NEWS.mdentry (sibling repo, test-only, not customer-visible). No go-pyresolverCHANGELOG.mdentry (test-only, per precedentee1a650).Refs rstudio/package-manager#18658
🤖 Generated with Claude Code