Skip to content

packaging: every extra but mcp is unbounded, and the lockfile hides the next 2.0 #103

Description

@Shashankss1205

Split out of #101, which fixed one instance of this and named the general case as separate work.

What happens

Every optional dependency except mcp is declared with a lower bound and no ceiling:

langchain-openai>=0.2     # openrouter, openai, novita, ollama
fastapi>=0.115            # server
uvicorn>=0.32             # server
real-ladybug>=0.15.3      # ladybug
anthropic>=0.40           # api
slack-bolt>=1.20          # slack
opentelemetry-api>=1.27   # otel
opentelemetry-sdk>=1.27
opentelemetry-exporter-otlp-proto-http>=1.27

Each is one major release away from the failure #101 documented: mcp>=1.2 resolved to 2.0.0 for anyone installing fresh, mcp.server.fastmcp no longer existed there, and grapharc[mcp] was broken on arrival.

Why nothing catches it

uv.lock pins the working versions, so the dev environment and every local pytest resolve to what already works. Only a resolver starting from pyproject.toml — that is, a real user running pip install grapharc[...] — sees the new major. The gap is structural, not an oversight: the lockfile's whole job is to keep development reproducible, and that is exactly what hides this.

CI's clean-environment wheel check is the one job that caught #101, and it caught it only because the broken import made a subpackage unwalkable. An extra whose SDK changed shape without breaking import would sail through.

What to consider

A scheduled job (weekly, plus workflow_dispatch) that resolves and installs from pyproject.toml with --upgrade rather than from the lockfile, then imports every subpackage the way ci.yml's build job already does. It must not gate pull requests — an upstream release is not a reason to redden someone's unrelated PR — so it should open or update an issue on failure instead.

Worth deciding alongside it: whether new extras get a ceiling by default. #101's pin is documented as deliberate (raising it means porting off FastMCP), which is the right shape for a bound — a ceiling with a reason attached, not a reflex.

Acceptance criteria

A resolve that ignores uv.lock and takes the newest release of every extra runs on a schedule, imports every subpackage from the built wheel, and reports a failure somewhere a maintainer will see it without it blocking unrelated pull requests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions