Skip to content

ci: gate on uv.lock staleness so CI's typecheck env can't drift from the lockfile - #268

Merged
cdeust merged 1 commit into
mainfrom
fix-uvlock-tree-sitter-pin-251
Jul 30, 2026
Merged

ci: gate on uv.lock staleness so CI's typecheck env can't drift from the lockfile#268
cdeust merged 1 commit into
mainfrom
fix-uvlock-tree-sitter-pin-251

Conversation

@cdeust

@cdeust cdeust commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #251.

uv.lock currently resolves tree-sitter-language-pack 1.13.5 — the same
version CI's typecheck job installs via the hash-pinned,
uv.lock-derived requirements/ci-typecheck.txt — so
uv sync --locked --all-extras && pyright mcp_server/ already reports
0 errors, 0 warnings, 0 informations today (evidence below). But nothing in
CI verified uv.lock itself stays current with pyproject.toml's declared
constraints; the existing "Check hash-pinned requirements match uv.lock"
step only compares the committed requirements/*.txt against whatever
uv.lock currently says — a stale lock and its stale export agree with
each other and that check stays green.

That gap is exactly how the original divergence shipped: Dependabot's deps
PR #218 widened tree-sitter-language-pack's upper bound in
pyproject.toml (<1.7 -> <1.14) but touched no other file — its pip
ecosystem entry rewrites the version specifier only, it does not re-run
uv lock. uv.lock kept resolving 1.6.2 (still valid under the wider
range — uv lock's incremental resolution prefers an existing pin over the
newest compatible one), so a contributor's uv sync --locked and CI's
then-unconstrained pip install -e ".[...]" silently diverged on the same
commit: 1 pyright error locally, 0 in CI.

Fix

Add a uv lock --check step to the lint job, running before the existing
hash-pinned-requirements check, so any future pyproject.toml constraint
change that isn't accompanied by a lock refresh fails the build at commit
time instead of resolving silently differently per install path.

Reproduction that the new gate would have caught the original drift

Checked out pyproject.toml + uv.lock as of 9e293baa (the commit right
after #218 widened the range) into a scratch directory:

$ uv lock --check
Using CPython 3.13.11
Resolved 196 packages in 186ms
The lockfile at `uv.lock` needs to be updated, but `--check` was provided. To update the lockfile, run `uv lock`.
$ echo "exit=$?"
exit=1

Reproduction that the current tree is already clean (issue's AC #1)

$ uv lock --check
Resolved 195 packages in 2ms
$ pyright --venvpath <shared .venv's parent> mcp_server/
0 errors, 0 warnings, 0 informations

(pyright 1.1.410, the CI pin; tree-sitter-language-pack 1.13.5 installed,
matching both uv.lock and CI's requirements/ci-typecheck.txt.)

$ pytest tests_py/core/test_ast_parser.py -q
19 passed in 0.46s

Boy-scout fix (same file, same dependency)

Corrected a stale comment on the tree-sitter-language-pack pin: it claimed
<1.7 (no such PyPI release exists — versions jump 1.6.3 -> 1.8.0) and
an AttributeError on parser.parse(...) traced to upstream issue #141
(get_parser/get_language API consistency). That upstream break only
affects callers building tree_sitter.Parser(get_language(name))
themselves — the changelog names get_parser(name) directly (what
ast_parser.py:88 calls) as the unaffected, documented path. Verified
against the actually-resolved 1.13.5 above (19/19 tests passing).

Deferred (outside this change's blast radius, filed)

Running actionlint against .github/workflows/ci.yml as an extra
precaution on the new step surfaced pre-existing SC2015/SC2034 shellcheck
findings in the unrelated test job's PostgreSQL-provisioning retry loops
(lines 55, 56, 64) — not reachable by any build/lint/test step this PR's own
change triggers, and not part of this repo's own gates (no actionlint
reference anywhere in .github/). Filed as
#267 per coding-standards.md §14.3
rather than folded in here (also avoids adding merge-conflict surface to the
same file across several other in-flight worktree branches).

Completion ledger

Path in diff Evidence
New Check uv.lock matches pyproject.toml step passes on current tree uv lock --checkResolved 195 packages in 2ms, exit 0
New step fails on the historical drifted state reproduced against 9e293baa's pyproject.toml+uv.lock → exit 1, "needs to be updated"
actionlint reports no new findings from the added step ran before/after; identical 4 pre-existing findings at line 45 (test job), none near the new step
pyproject.toml comment correction doesn't change resolution uv lock --check and generate_pip_constraints.py --check both pass unchanged after the edit (comment-only)
pyright 0-diagnostic gate holds under the resolved (1.13.5) environment pyright --venvpath ... mcp_server/0 errors, 0 warnings, 0 informations
ast_parser.py's get_parser(...).parse(...) call is functionally sound under 1.13.5 pytest tests_py/core/test_ast_parser.py -q19 passed
Edited TOML is syntactically valid tomllib.load() round-trip
Edited YAML is syntactically valid yaml.safe_load() round-trip

Not done in this PR (by design, not deferral)

mcp_server/core/ast_parser.py:88's SupportedLanguage literal narrowing
(one of the three "or" options in #251's AC, and the subject of the
overlapping #249/#253 duplicate issues) is left untouched: another in-flight
worktree branch is already addressing it under #249, and #251's own
acceptance criteria is satisfied by the CI-reproducibility fix above (an
"or", not an "and").

Test plan

https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u

@cdeust
cdeust force-pushed the fix-uvlock-tree-sitter-pin-251 branch from 0d221d4 to 0d6c710 Compare July 29, 2026 22:10
…the lockfile (#251)

Adds a `uv lock --check` step to the lint job, before the existing
hash-pinned-requirements check, so a future pyproject.toml constraint
change that isn't accompanied by a lock refresh fails at commit time
instead of resolving silently differently per install path (the
drift that shipped as #251).

Rebased onto main (7f3ebf9), which had since landed #253/#261 --
main's tree-sitter-language-pack floor bump (>=1.12.5) already
supersedes this branch's own stale-comment correction on the same
line, so the redundant/superseded block is dropped rather than kept
alongside main's (duplicate package-version lines is what broke
`uv lock --check`: pyproject.toml carried two conflicting specifiers
for the same package after an earlier bad merge).

Reworded the new step's own comment: it quoted the literal historical
`pip install -e ".[...]"` command as prose, which collides with
tests_py/scripts/test_typecheck_env_parity.py::
test_job_does_not_pip_install_an_extra_range -- a whole-file
assertNotIn added on main after this branch was cut, forbidding that
exact substring anywhere in ci.yml (including comments, since a
contributor could copy-paste it as real usage). Reworded to describe
the same historical fact without the literal substring; the assertion
itself is untouched per coding-standards.md §6 (never weaken a gate
to make it pass).

Verified on this tree:
- `uv lock --check` -> `Resolved 195 packages in 3ms`, exit 0
- `pytest tests_py/scripts/test_typecheck_env_parity.py -q` -> 9 passed
- `pytest tests_py/core/test_ast_parser.py -q` -> 19 passed
- `python scripts/check_doc_claims.py` -> OK
- `python scripts/generate_repo_badges.py --check` -> OK
- `python scripts/generate_pip_constraints.py --check` -> OK
- `actionlint .github/workflows/ci.yml` -> no findings
- `grep 'pip install -e ".\[' .github/workflows/ci.yml` -> no match (exit 1)
- YAML/TOML round-trip parse clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
@cdeust
cdeust force-pushed the fix-uvlock-tree-sitter-pin-251 branch from 0d6c710 to 7fc0b5c Compare July 29, 2026 22:40
@cdeust
cdeust merged commit 56f2f4f into main Jul 30, 2026
19 checks passed
@cdeust
cdeust deleted the fix-uvlock-tree-sitter-pin-251 branch July 30, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

uv.lock pins tree-sitter-language-pack 1.6.2; the pyright gate is only clean on CI's pip-resolved 1.13.5

1 participant