fix(launcher): strip extras from the pip constraints file - #339
Merged
Conversation
`ensure_all_deps` passes BASE_PACKAGES verbatim as the `-c` constraints file, and `psycopg[binary]==3.3.4` carries an extra. pip documents constraints files as version-only and now rejects extras outright, so the whole ML resolve aborted with "ERROR: Constraints cannot have extras" — sentence-transformers and flashrank never installed. The failure was silent: only the ML install passes constraints, so the base stack installed clean and the FlashRank re-ranker was simply absent, leaving recall on first-stage scores with no failing check — the same observable shape as the 2026-07-10 FlashRank incident. `pip_install` now normalizes each constraint through `constraint_without_extras`. This restores the parameter's own documented contract (`name==ver`) rather than changing it: a constraint pins the VERSION a shared transitive resolves to, and pip applies it to the distribution however its extras were requested — the install target still carries `[binary]`. Measured 2026-08-02 on pip 26.0.1/py3.14, reproduced on pip 25.2/py3.13, so the affected range is not pip-26-only; both accept the stripped file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cdeust
added a commit
that referenced
this pull request
Aug 2, 2026
Ships #339. Bumps every version site the release gates guard, so the fix actually lands in plugin installs rather than stopping at the tag: pyproject.toml, uv.lock (root package), .claude-plugin/plugin.json, .claude-plugin/marketplace.json (metadata + the hypermnesia-mcp entry), server.json (version + packages[0]), manifest.json, package.json, and the regenerated assets/badge-version.svg. Gates green: check_marketplace_pins.py ("All marketplace pins current"), check_doc_claims.py, check_venv_lock_parity.py; tests_py/scripts 578 passed; ruff clean. Co-Authored-By: Claude Opus 5 <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.
The bug
ensure_all_depspassesBASE_PACKAGESverbatim as the-cconstraints file (launcher_deps.py:318), and one entry carries an extra —psycopg[binary]==3.3.4(launcher_pins.py:89). pip has always documented constraints files as version-only and now rejects extras outright, so the entire ML resolve aborts:sentence-transformersandflashranknever install.Why it went unnoticed
Only the ML install passes constraints. The base stack installs clean, so nothing fails visibly — the FlashRank re-ranker is simply absent, and
recallsilently degrades to first-stage-only scores. Same observable shape as the 2026-07-10 FlashRank incident.Hit on a real plugin install (fresh
hypermnesia-mcp@cortex-plugins4.17.1, macOS).The fix
pip_installnormalizes each constraint through the newconstraint_without_extrasbefore writing the file.This restores the parameter's own documented contract rather than changing it — its docstring already promised
name==ver. A constraint pins the VERSION a shared transitive resolves to (numpy, via sentence-transformers), and pip applies it to the distribution however its extras were requested. Extras belong on the install target, which still carries[binary].Version range — not pip-26-only
I first attributed this to pip 26. That was wrong: pip 25.2 rejects it too, found while validating against the project venv. The affected population is considerably wider than the pip-26 leading edge. Both measurements are now in the source comment and CHANGELOG.
Verification — external signals
pip itself judges the generated file:
ERROR: Constraints cannot have extrasCommand:
pip install --dry-run --no-index -c <BASE_PACKAGES> flashrank==0.2.10, run on pip 26.0.1/py3.14 and pip 25.2/py3.13.BASE_PACKAGESlist so a future extra cannot reintroduce this.tests_py/scripts/: 578 passed, 121 subtests.ruff check+ruff format --check: clean.launcher_deps_install.pystays within the repo's 300-line cap (300/300).Note for release
Per CLAUDE.md, this delivers nothing to installs until the marketplace pins move — the fix needs a release +
.claude-plugin/marketplace.jsonbump withscripts/check_marketplace_pins.pyexiting 0.🤖 Generated with Claude Code