Enforce pyproject.toml version caps with dependabot ignore rules (dependabot silently widens caps)
What is the technical debt you think should be addressed?
Dependabot does not treat version caps in pyproject.toml as policy — it
rewrites them to reach the latest release.
Incident. After the grouped python-deps config shipped (#6566), the first
grouped run (PR #6587, 2026-09-18) immediately widened
mcp >=1.28.1,<2.0 → mcp >=2.2.0,<3.0. mcp 2.x is breaking (HTTP redirect
origin restriction, OAuth issuer validation, 30-min session expiry). Per-package
runs had not widened it in 7.5 weeks, so the grouping change masked the
mechanism.
Mechanism (verified in dependabot-core source). For any non-lockfile-only
update strategy, the checker takes the :own-requirements-unlock path
(update_all_versions.rb / group_update_creation.rb, identical logic, tried
first). Its version discovery — latest_version → fetch_latest_release
(package_latest_version_finder.rb) — never consults the existing requirement;
only the lockfile-only path
(latest_resolvable_version_with_no_unlock → filter_out_of_range_versions)
does. The updater then mechanically rewrites the range around the target
(BumpVersions: >=1.28.1,<2.0 → >=2.2.0,<3.0).
So any dependency with a < cap is one dependabot run away from having the cap
widened. Current caps (9, across two manifests, post-#6587):
| Dependency | Cap | Root cause issue |
|---|---|---|
| mcp | <2.0 |
— |
| fastapi (root) | >=0.115.0,<0.137 (CI workaround) |
#6594 |
| fastapi (src/ogx_api) | >=0.136.3,<1.0 |
— |
| elasticsearch | >=9.5.1,<10.0.0 |
— |
| google-genai | >=2.23.0,<3 |
— |
| transformers | >=4.57.2,<5.0.0 |
#6588 |
| setuptools | <81 |
— |
| openai | >=2.41.0,<2.44 (recorder bug) |
#6593 |
| langchain-openai | >=1.2.2,<1.3 (recorder bug) |
#6593 |
The supported enforcement mechanism is the ignore directive in
.github/dependabot.yml. Group update-types is not sufficient — it only moves
a major update out of the group; the individual PR would still widen the cap.
What is the benefit of addressing this technical debt?
- Caps actually hold: dependabot may only move the floor within the cap
(e.g. mcp 1.28.1 → 1.30.x, still
<2.0). - No more manual reverts of dependabot PRs that widened a cap (#6587 already needed three: mcp, openai/langchain-openai, fastapi).
- A documented, repeatable pattern for every future pin.
Other thoughts
Proposed change (small config PR against main, no lockfile changes):
.github/dependabot.yml: addignorerules mirroring every cap, in both uv sections:Section Ignore Versions /fastapi >=0.137.0/openai >=2.44.0/langchain-openai >=1.3.0/mcp >=2.0.0/elasticsearch >=10.0.0/google-genai >=3.0.0/transformers >=5.0.0/setuptools >=81/src/ogx_apifastapi >=1.0.0The PR lands directly on
mainso the next weekly dependabot run is protected immediately. The openai / langchain-openai / fastapi>=0.137.0ignores are preemptive until #6587 merges — harmless, since they only ignore versions no current manifest can reach.pyproject.toml(root + src/ogx_api): comment on each capped dependency noting the cap is enforced by the matchingignorerule and both must change in the same PR.
Trade-off (documented in the YAML): ignore also filters security
advisories (filter_ignored_versions runs in lowest_security_fix_version). A
CVE fixed only at/above a cap will not get a dependabot security PR — a manual
cap+ignore lift is required in that case.
Related: #6587 (incident PR), #6593 (openai <2.44 cap / API recorder), #6594
(fastapi <0.137 cap / route introspection), #6588 (transformers 5.x migration).
Source: ogx-ai/ogx