Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .clusterfuzzlite/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
# Runtime dependencies of the modules under test, hash-pinned from uv.lock
# (scripts/generate_pip_constraints.py). The harnesses import mcp_server
# modules, so their imports must resolve.
pip3 install --require-hashes -r "$SRC/cortex/requirements/ci-sqlite-min.txt"
# --no-deps: the file is the complete, uv-resolved dependency graph — pip
# must install it as-is rather than re-deriving it from metadata, which
# breaks the moment pyproject.toml's [tool.uv] override-dependencies
# steers a package past a bound another package's metadata still declares
# (issue: PR #332, mpmath 1.4.1 vs sympy's `mpmath<1.4`).
pip3 install --no-deps --require-hashes -r "$SRC/cortex/requirements/ci-sqlite-min.txt"
pip3 install --no-deps -e "$SRC/cortex"

# compile_python_fuzzer is provided by the base image. It wraps each harness
Expand Down
7 changes: 6 additions & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ COPY mcp_server ./mcp_server
# index serves for it. The CPU-only torch build is carried by the file (see
# [[tool.uv.index]] in pyproject.toml) rather than by an --index-url flag
# here; the rationale for CPU-only is unchanged and lives in ../Dockerfile.
# --no-deps: the file is the complete, uv-resolved dependency graph — pip
# must install it as-is rather than re-deriving it from metadata, which
# breaks the moment pyproject.toml's [tool.uv] override-dependencies
# steers a package past a bound another package's metadata still declares
# (issue: PR #332, mpmath 1.4.1 vs sympy's `mpmath<1.4`).
COPY requirements/devcontainer.txt /tmp/requirements.txt
RUN pip install --no-cache-dir --require-hashes -r /tmp/requirements.txt
RUN pip install --no-cache-dir --no-deps --require-hashes -r /tmp/requirements.txt

# The project itself, editable so a contributor's edits take effect without
# a rebuild. --no-deps because the hashed file above is the complete
Expand Down
15 changes: 12 additions & 3 deletions .github/actions/test-suite/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,22 @@ runs:
- name: Install dependencies
shell: bash
# Hash-pinned from uv.lock (scripts/generate_pip_constraints.py).
# --no-deps on the project install because the file above IS the
# complete dependency graph; re-resolving here would be unpinned.
# --no-deps on BOTH installs: the file is the complete, uv-resolved
# dependency graph, so pip must not re-derive it. Without --no-deps
# on the requirements-file install too, pip re-validates every listed
# package's declared metadata dependencies against the rest of the
# file — which breaks the moment pyproject.toml's [tool.uv]
# override-dependencies steers a package (mpmath) past a bound
# another package's metadata still declares (sympy's `mpmath<1.4`):
# uv's resolver honours the override, but the exported
# requirements.txt format cannot carry it, so pip's own
# re-derivation sees only the unresolved conflict (issue: PR #332,
# `ResolutionImpossible` on every install job).
# requirements/ci-postgresql.txt inline: this action's sole caller
# (ci.yml's `test` matrix) always installs it — issue #392 removed the
# only other caller, which installed requirements/release.txt instead.
run: |
pip install --require-hashes -r requirements/ci-postgresql.txt
pip install --no-deps --require-hashes -r requirements/ci-postgresql.txt
pip install --no-deps -e .

# Populate the HuggingFace cache before the (offline) test run. A transient
Expand Down
44 changes: 33 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,18 @@ jobs:
# Measured 2026-07-28 locally: 8 skips from tree-sitter, 1 from leidenalg.
- name: Install dependencies (no postgresql extra)
# Hash-pinned from uv.lock (scripts/generate_pip_constraints.py).
# --no-deps on the project install because the file above IS the
# complete dependency graph; re-resolving here would be unpinned.
# --no-deps on BOTH installs: the file is the complete, uv-resolved
# dependency graph, so pip must not re-derive it. Without --no-deps
# here, pip re-validates every listed package's declared metadata
# dependencies against the rest of the file — which breaks the
# moment pyproject.toml's [tool.uv] override-dependencies steers a
# package (mpmath) past a bound another package's metadata still
# declares (sympy's `mpmath<1.4`): uv's resolver honours the
# override, but the exported requirements.txt format cannot carry
# it, so pip's own re-derivation sees only the unresolved conflict
# (issue: PR #332, `ResolutionImpossible` on every install job).
run: |
pip install --require-hashes -r requirements/ci-sqlite.txt
pip install --no-deps --require-hashes -r requirements/ci-sqlite.txt
pip install --no-deps -e .

# Retry-with-backoff, fail-loudly: see the `test` job's pre-download step
Expand Down Expand Up @@ -288,10 +296,11 @@ jobs:

- name: Install dependencies (no postgresql extra)
# Hash-pinned from uv.lock (scripts/generate_pip_constraints.py).
# --no-deps on the project install because the file above IS the
# complete dependency graph; re-resolving here would be unpinned.
# --no-deps on BOTH installs — see the "Install dependencies (no
# postgresql extra)" step above (SQLite job) for why the
# requirements-file install needs it too, not just the local -e .
run: |
pip install --require-hashes -r requirements/ci-sqlite-min.txt
pip install --no-deps --require-hashes -r requirements/ci-sqlite-min.txt
pip install --no-deps -e .

# Import smoke: the modules that previously crashed at load on Windows
Expand Down Expand Up @@ -399,8 +408,11 @@ jobs:
key: ${{ runner.os }}-hf-all-MiniLM-L6-v2

- name: Install the release dependency set (hash-pinned)
# --no-deps on BOTH installs — see the SQLite job's "Install
# dependencies" step above for why the requirements-file install
# needs it too, not just the local -e .
run: |
pip install --require-hashes -r requirements/release.txt
pip install --no-deps --require-hashes -r requirements/release.txt
pip install --no-deps -e .

# Retry-with-backoff, fail-loudly: see the `test` job's pre-download
Expand Down Expand Up @@ -437,7 +449,10 @@ jobs:
# Pinned: ruff's formatter output changes across minor versions
# (0.15.6 vs 0.15.20 divergence broke Lint on PR #83). The repo is
# formatted with 0.15.20; bump this pin and reformat together.
run: pip install --require-hashes -r requirements/lint.txt
# --no-deps: the file is the complete, uv-resolved dependency graph
# (see the SQLite job's "Install dependencies" step above) — pip
# must install it as-is rather than re-deriving it from metadata.
run: pip install --no-deps --require-hashes -r requirements/lint.txt

- name: Check formatting
run: ruff format --check .
Expand Down Expand Up @@ -612,13 +627,17 @@ jobs:
# flashrank (core reranker) + sqlite-vec live outside dev/postgresql/
# codebase; [otel] resolves the opentelemetry exporter imports.
- name: Create .venv with the full type-check environment
# --no-deps on every requirements-file install here — see the
# SQLite job's "Install dependencies" step above for why: each
# file is the complete, uv-resolved dependency graph, and pip must
# not re-derive it from metadata.
run: |
python -m venv .venv
.venv/bin/pip install --require-hashes -r requirements/ci-typecheck.txt
.venv/bin/pip install --no-deps --require-hashes -r requirements/ci-typecheck.txt
.venv/bin/pip install --no-deps -e .
# Pin pyright — diagnostic output drifts between releases, so a
# zero-diagnostic tree is only comparable against the pinned version.
.venv/bin/pip install --require-hashes -r requirements/typecheck-tool.txt
.venv/bin/pip install --no-deps --require-hashes -r requirements/typecheck-tool.txt

# The gate's verdict is a property of THIS environment, so the log has to
# name it. Issue #253: a contributor and CI read two different
Expand Down Expand Up @@ -656,7 +675,10 @@ jobs:
python-version: "3.12"

- name: Install build tools
run: pip install --require-hashes -r requirements/packaging.txt
# --no-deps: the file is the complete, uv-resolved dependency graph
# (see the SQLite job's "Install dependencies" step above) — pip
# must install it as-is rather than re-deriving it from metadata.
run: pip install --no-deps --require-hashes -r requirements/packaging.txt

- name: Build sdist and wheel
run: python -m build
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ jobs:
python-version: "3.12"

- name: Install build tools
run: pip install --require-hashes -r requirements/packaging.txt
# --no-deps: the file is the complete, uv-resolved dependency graph
# — pip must install it as-is rather than re-deriving it from
# metadata (see ci.yml's "Install dependencies" steps for why:
# pyproject.toml's [tool.uv] override-dependencies is a uv-only
# mechanism the exported requirements.txt format cannot carry).
run: pip install --no-deps --require-hashes -r requirements/packaging.txt

- name: Build sdist and wheel
run: python -m build
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,14 @@ COPY tests_py ./tests_py
# `--upgrade pip build` is gone: it was itself an unpinned install, and
# `build` was never invoked in this file. The base image is digest-pinned,
# so its pip is a known quantity.
# --no-deps on both requirements-file installs below: each file is the
# complete, uv-resolved dependency graph — pip must install it as-is
# rather than re-deriving it from metadata, which breaks the moment
# pyproject.toml's [tool.uv] override-dependencies steers a package past
# a bound another package's metadata still declares (issue: PR #332,
# mpmath 1.4.1 vs sympy's `mpmath<1.4`).
COPY requirements/runtime-postgresql.txt requirements/packaging.txt /tmp/
RUN pip install --no-cache-dir --require-hashes -r /tmp/runtime-postgresql.txt
RUN pip install --no-cache-dir --no-deps --require-hashes -r /tmp/runtime-postgresql.txt

# The project itself, as a built wheel installed with --no-deps.
#
Expand All @@ -84,7 +90,7 @@ RUN pip install --no-cache-dir --require-hashes -r /tmp/runtime-postgresql.txt
#
# --no-isolation so the build backend is the hashed hatchling from
# packaging.txt rather than one fetched from PyPI mid-build.
RUN pip install --no-cache-dir --require-hashes -r /tmp/packaging.txt && \
RUN pip install --no-cache-dir --no-deps --require-hashes -r /tmp/packaging.txt && \
python -m build --wheel --no-isolation --outdir /tmp/dist . && \
pip install --no-cache-dir --no-deps /tmp/dist/*.whl

Expand Down
7 changes: 6 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,15 @@ RUN cd /opt/claude-code && npm ci --omit=dev --ignore-scripts
# copies one directory and no COPY names the Python version. See the root
# Dockerfile for the incident that rule comes from: a literal
# .../python3.13/site-packages path broke on every base-image bump.
# --no-deps: the file is the complete, uv-resolved dependency graph — pip
# must install it as-is rather than re-deriving it from metadata, which
# breaks the moment pyproject.toml's [tool.uv] override-dependencies
# steers a package past a bound another package's metadata still declares
# (issue: PR #332, mpmath 1.4.1 vs sympy's `mpmath<1.4`).
COPY requirements/docker-runtime.txt /tmp/requirements.txt
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir --require-hashes -r /tmp/requirements.txt
RUN pip install --no-cache-dir --no-deps --require-hashes -r /tmp/requirements.txt

# Cortex itself: --no-deps because every dependency was just installed from
# the hashed file above, and re-resolving here would reintroduce unpinned
Expand Down
42 changes: 42 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,48 @@ constraint-dependencies = [
"onnxruntime<1.24 ; python_full_version < '3.11'",
]

# ── mpmath — real, named upstream incompatibility with a known ceiling ───
#
# sympy 1.14.0 (latest, no newer release exists) declares
# `mpmath<1.4,>=1.1.0`. Dependabot's mpmath 1.3.0 -> 1.4.1 bump therefore
# fails uv's default resolution: sympy pins the lock back to 1.3.0.
#
# CORRECTION (2026-08-10 review): this bound is NOT merely precautionary.
# sympy's maintainer documents a real, active dependency on a deprecated
# mpmath 1.4.x API (`mpf_log`, replaced by `mpf_ln`) — sympy 1.14.0 still
# calls it, and an mpmath 1.4.0 ALPHA once broke `import sympy` outright
# before that was fixed. `sympy.sqrt(8).evalf()` importing and evaluating
# correctly under mpmath 1.4.1 (verified 2026-08-01) is a sample of one
# code path, not proof the two are compatible in general — it does not
# cover whatever else in sympy's ~large surface still calls deprecated
# mpmath internals. Nothing in this repo imports sympy or mpmath
# directly (both arrive transitively through torch), which is exactly
# why this override is acceptable at all: the deprecated-API surface
# sympy still touches is not exercised by anything this repo runs.
#
# Bounded, not open-ended: sympy's maintainer states the NEXT sympy
# release (1.15) will cap at `mpmath<1.5`, precisely because an mpmath
# 1.4.0 alpha broke sympy import once already and they do not want a
# repeat with a hypothetical mpmath 1.5. This override matches that
# stated ceiling rather than leaving the door open for
# `uv lock --upgrade` (routine, not `--upgrade-package mpmath`) to pull
# a version sympy's own maintainer has already said may not work.
# Remove this override once sympy ships a release with mpf_log removed
# and widens its own declared range past 1.4.
# source: sympy/sympy#29231 (github.com/sympy/sympy/issues/29231),
# read 2026-08-10 — maintainer (oscarbenjamin) comments:
# "sympy 1.14.0 uses API that is deprecated in mpmath 1.4.0"
# (mpf_log); "mpmath 1.4.0 alpha 0 completely broke sympy to the
# extent that `import sympy` failed"; "I would say that the new
# [sympy 1.15] release should cap `mpmath < 1.5`".
# source: PyPI JSON API https://pypi.org/pypi/sympy/json, read 2026-08-10
# (latest 1.14.0, requires_dist still `mpmath<1.4,>=1.1.0`)
# source: uv settings reference, "override-dependencies" —
# https://docs.astral.sh/uv/reference/settings/#override-dependencies
override-dependencies = [
"mpmath>=1.4.1,<1.5",
]

# ── CPU-only torch, resolved through the lock ────────────────────────────
#
# Every container in this repo installs torch from the PyTorch CPU index,
Expand Down
6 changes: 3 additions & 3 deletions requirements/ci-postgresql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -639,9 +639,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/ci-sqlite-min.txt
Original file line number Diff line number Diff line change
Expand Up @@ -605,9 +605,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/ci-sqlite.txt
Original file line number Diff line number Diff line change
Expand Up @@ -639,9 +639,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/ci-typecheck.txt
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/devcontainer.txt
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/docker-runtime.txt
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/release.txt
Original file line number Diff line number Diff line change
Expand Up @@ -603,9 +603,9 @@ mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mpmath==1.3.0 \
--hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
--hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
mpmath==1.4.1 \
--hash=sha256:dc4f0ea2304480d4a9a48a94c1020571558ade522b44a6912efac63a586e140f \
--hash=sha256:efd6d1b75f09d69524a67609949812668b28e81ecbfe0ab449ced8c13e92642e
# via sympy
narwhals==2.24.0 ; python_full_version >= '3.11' \
--hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \
Expand Down
Loading