# Upper bounds are deliberate. Every line here was `>=` with no ceiling, so a
# fresh `pip install -r requirements.txt` resolved to whatever was newest that
# day — and several already crossed a major release without anyone choosing to:
# textual 0.61 -> 8.2, starlette 0.37 -> 1.3, pytest 8 -> 9. Nothing broke, but
# nothing prevented it either; the failure would have been at runtime, not at
# install.
#
# The floors are unchanged (they exclude nobody who works today). The ceilings
# are the next major above a set verified green: 177 tests passing, 2026-07-27.
# Raise one deliberately when you have run against it.

# Three floors raised 2026-09-30 (gap 37e6bf3f1e21), matching pyproject.toml:
# cryptography 49.0.0 (CVE-2026-69249/69248), aiohttp 3.14.0 (CVE-2026-50269),
# starlette 1.0.1 (CVE-2026-48710, below). The installed venv already sat above
# all three (cryptography 49.0.0, aiohttp 3.14.3, starlette 1.6.0).
textual>=0.61.0,<9
psycopg2-binary>=2.9.0,<3
cryptography>=49.0.0,<50
aiohttp>=3.14.0,<4

# Floor raised to match willow-mcp's own pin (`mcp>=1.28.1,<2.0.0`). Grove and
# willow-mcp speak the same protocol to each other, so they should not be able
# to resolve to different SDK majors. The MCP 2026-07-28 revision is the largest
# since launch and breaks the handshake, session IDs and the version string, so
# an unbounded pin here was a live risk rather than a theoretical one.
mcp>=2.0.0,<3.0.0

starlette>=1.0.1,<2
uvicorn>=0.30.0,<1

# uvicorn has no bundled WebSocket implementation; without one it answers
# every WS upgrade with a bare HTTP 404 rather than an error (measured
# 2026-09-22T06:29Z, dispatch 452ED95B: #81's live deploy 404'd on
# /events/{seat} for exactly this reason, on a venv this file had never
# named). `websockets` is uvicorn's own default (its `standard` extra pins
# `websockets>=13.0`) and the narrower choice over `uvicorn[standard]`,
# which would also add httptools/uvloop/watchfiles/python-dotenv/pyyaml.
websockets>=13.0,<17

pytest>=8.0.0,<10

# The `nestor` CLI, which grove/nestor_client.py spawns as `nestor serve`
# (MCP over stdio) and which .mcp.json serves the session store with. It is a
# console script, not an import — nothing in grove/ does `import nestor`. Kept
# in the development install so the seam is live locally; the packaged install
# leaves it to the `nestor` extra, because Grove must still render the
# unreachable state (INVARIANTS.md §1) when the binary is absent. Pre-1.0, so
# the ceiling is the next minor above the version verified green: 0.16.2.
nestor-meaning>=0.16.2,<0.17
