fix(deps): pin tree-sitter-language-pack to the range where get_parser().parse(bytes) works (#253) - #261
Merged
Merged
Conversation
cdeust
force-pushed
the
fix-pyright-tree-sitter-tree-253
branch
from
July 29, 2026 19:01
a8b96e8 to
20f962e
Compare
…r().parse(bytes) works (#253) The pyright gate's verdict depended on which installer built the environment: CI installs the hash-pinned export of uv.lock, the documented developer install resolved pyproject.toml's >=0.24.0,<1.14 range, and the two landed seven minor versions apart on tree-sitter-language-pack — one error in one environment, zero in the other, on the same commit. Probing every published wheel found a live defect the old floor admitted: 1.9.0-1.12.2 return a builtins.Parser whose parse(source: str) rejects bytes, so get_parser("python").parse(b"...") raises TypeError uncaught and codebase_analyze crashes. The floor moves to 1.12.5, the first release where the whole call chain is valid and type-checkable. - pyproject: floor 1.12.5, with the measured per-release type surface - ast_parser: _EXTRACTORS keyed by the pack's SupportedLanguage literal, AST_SUPPORTED derived from it (deleting an unreachable fallback arm), _is_ast_language TypeGuard narrows before get_parser - CONTRIBUTING/CLAUDE: both documented installs resolve from uv.lock - ci.yml: the Type Check job prints the versions its verdict depends on - tests: language contract (parses with every declared grammar) and typecheck-env parity (docs vs pip_constraint_sets vs ci.yml) Boy-scout (§14): four pre-existing shellcheck findings in the touched ci.yml (SC2015 x3, SC2034) fixed; the suite's 3 sqlite datetime DeprecationWarnings are outside this change's blast radius and filed as Closes #253 Closes #249 Closes #251 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
Rebase bookkeeping after #259 merged; no behaviour change. The earlier rebase resolved the six suite-size files to main's side wholesale, which deleted 49 lines of this branch's own documentation — including the '### Reproducing the pyright gate locally' section that tests_py/scripts/test_typecheck_env_parity.py asserts, failing 5 of its 9 cases. This time the branch's doc additions are re-applied as a PATCH against the new main (git apply --3way) so both sides' prose survives; the only conflicts were the hard-coded count, resolved to the current figure. Count is the measured absolute (6548), not a delta from main's advertised number: CI proved on #259 that this machine's collection matches CI's canonical exactly, so the delta arithmetic I used before was the wrong instrument. Verified: test_typecheck_env_parity.py 9 passed; check_doc_claims.py OK; generate_repo_badges.py --check OK; .bestpractices.json parses and carries no conflict markers (the class that shipped once before, now gated by check_no_conflict_markers).
cdeust
force-pushed
the
fix-pyright-tree-sitter-tree-253
branch
from
July 29, 2026 19:38
c50980d to
b3a023c
Compare
cdeust
added a commit
that referenced
this pull request
Jul 29, 2026
origin/main advanced to 6548 tests (PR #261, tree-sitter-language-pack pin + its own test additions) between this branch's creation and this merge, superseding my prior 6526->6527 resync. Merged origin/main, resolved the doc-count conflicts by taking main's committed values, then re-derived the true paired count on the FULLY MERGED tree via `pytest --collect-only -q` (6549 = 6548 + this branch's one new test), not by arithmetic on stale numbers. Resynced the same 6 files + regenerated assets/badge-tests.svg. Both check_doc_claims.py --test-count 6549 and generate_repo_badges.py --check --test-count 6549 verified green locally, and the full suite (6549 tests) passes on the merged tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
added a commit
that referenced
this pull request
Jul 29, 2026
#250) (#272) * fix(shared): kill 9 real json_native mutants, document 5 as equivalent (#250) Scoped mutmut run on the committed [tool.mutmut] scope (mcp_server/shared/ json_native.py) reproduced exactly the reported 14 survivors: 9 were a real test gap on the tolist()-failure debug log (format string + args never asserted), 5 are typing.cast/codec-name mutations that are provably equivalent at runtime (verified via inspect.getsource(typing.cast) and codecs.lookup identity, not just asserted). - tests_py/shared/test_json_native.py: new TestTolistFailureLogging test forces the tolist() exception path and asserts the exact log record (format string + both args via caplog), killing mutants 34-42. - mcp_server/shared/json_native.py: documented the 5 remaining survivors as equivalent at their use sites (§12.1), and split the 60-line to_json_native (over this repo's 40-line/coding-standards' 50-line cap) into _decode_bytes/_coerce_number/_tolist_fallback + a thin dispatcher — each helper under 20 lines, existing 14 tests passing byte-for-byte unchanged as the behavior-preservation proof (boy-scout: the function was already over cap before this change). Re-run after the split: 51 mutants, 0 surviving non-equivalent (same 5 equivalents renumbered under the new helper names). Fixes #250 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u * docs: resync advertised test count 6526 -> 6527 (#250) The new TestTolistFailureLogging test in the prior commit adds exactly one collected test (paired count verified locally and against CI's own `pytest --collect-only` output: both report 6527). This repo hard-codes the suite size in 6 files plus a committed badge SVG, checked by a blocking CI gate (`scripts/check_doc_claims.py` / `generate_repo_badges.py --check`) — CI's Test (Python 3.12) leg correctly failed on the stale 6526 claim. Resynced: README.md (x2), CONTRIBUTING.md (x2), CLAUDE.md, docs/ASSURANCE-CASE.md, .bestpractices.json (x4), assets/badge-tests.svg (regenerated via scripts/generate_repo_badges.py --test-count 6527). Both gates verified green locally with the same --test-count CI uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u * docs: resync test count 6548 -> 6549 after merging main (#250) origin/main advanced to 6548 tests (PR #261, tree-sitter-language-pack pin + its own test additions) between this branch's creation and this merge, superseding my prior 6526->6527 resync. Merged origin/main, resolved the doc-count conflicts by taking main's committed values, then re-derived the true paired count on the FULLY MERGED tree via `pytest --collect-only -q` (6549 = 6548 + this branch's one new test), not by arithmetic on stale numbers. Resynced the same 6 files + regenerated assets/badge-tests.svg. Both check_doc_claims.py --test-count 6549 and generate_repo_badges.py --check --test-count 6549 verified green locally, and the full suite (6549 tests) passes on the merged tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
cdeust
added a commit
that referenced
this pull request
Jul 29, 2026
…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
added a commit
that referenced
this pull request
Jul 29, 2026
… CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed #249 (the resolver-dependent typecheck defect this branch was also fixing) via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
8 tasks
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
…the lockfile (#251) (#268) 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 Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
… CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
… CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
… CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
… CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
… CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
added a commit
that referenced
this pull request
Jul 30, 2026
…eric + close Swift/Rust test gaps (follow-on to #249, closed by #261) (#270) * fix(types): narrow ast_parser's language str to a Literal, closing the resolver-dependent typecheck gate (#249) mcp_server/core/ast_parser.py:88 passed a plain `str` to tree_sitter_language_pack.get_parser(), whose declared parameter type differs across releases (a `SupportedLanguage` Literal in 1.6.2 vs plain `str` in 1.13.5) -- so pyright's zero-diagnostic verdict was a property of which version pip/uv resolved, not of this source. AST_SUPPORTED is now a Literal union narrowed via a TypeGuard instead of a bare set[str], verified clean under both the current uv.lock resolution and a standalone 1.6.2 install (the only residual error there is 1.6.2's own SupportedLanguage omitting "csharp" -- a pre-existing grammar-availability gap in that release, not a narrowing defect). Boy-scout pass on the touched file (mutation-tested, tools/mutation_check.sh equivalent, scoped to ast_parser.py + ast_extractor_registry.py): removed the flat `calls` list every per-language extractor computed and parse_file_ast immediately discarded (superseded by calls_per_function, never itself consumed), and closed the resulting zero test coverage for Swift/Rust parsing plus gaps in is_available, _node_text, _extract_module_doc, content_hash length, and calls_per_function's populated content. Filed #269 for the 50 mutmut "survived" reports left after that pass, which are proven false positives (verified directly via MUTANT_UNDER_TEST against the full test selection): mutmut's coverage attribution undercounts callers of a dispatch table built once, eagerly, at module-import time. Test count 6526 -> 6549 (+23), resynced across README/CONTRIBUTING/ CLAUDE.md/ASSURANCE-CASE.md/.bestpractices.json/badge-tests.svg via scripts/check_doc_claims.py + scripts/generate_repo_badges.py. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u * chore: reconcile rebase onto main (#261) — recompute suite count, fix CHANGELOG claim Rebasing onto main pulled in #261/#253/#251, which independently closed via the same TypeGuard-narrowing mechanism, already keyed off the language pack's own SupportedLanguage type and with AST_SUPPORTED derived from _EXTRACTORS rather than restated. This branch's own duplicate _SupportedLanguage/_is_ast_supported/AST_SUPPORTED-via-get_args mechanism is dropped in favour of main's (ast_parser.py, ast_extractor_registry.py); this branch's actual surviving contribution — removing the dead `calls` tuple element from every extractor and adding the missing Swift/Rust/ is_available/_node_text/_extract_module_doc/content_hash/calls_per_function test coverage — is preserved and now sits on top of main's mechanism cleanly (auto-merged with no conflicts in ast_extractor_registry.py). CHANGELOG's #249 entry rewritten: it no longer claims to close #249 (already closed by #261) and instead describes only the boy-scout dead-code-removal and test-coverage work this branch still adds. Suite count recomputed from a live `pytest --collect-only -q` on this machine (6572, up from main's 6549 by the 23 tests this branch adds) per the standing rule that the count is a measured absolute, not a delta from an advertised number: CLAUDE.md, README.md, CONTRIBUTING.md, .bestpractices.json, docs/ASSURANCE-CASE.md, assets/badge-tests.svg. Verified: scripts/check_doc_claims.py --test-count 6572 and scripts/generate_repo_badges.py --check --test-count 6572 both green; tests_py/scripts/test_typecheck_env_parity.py, test_check_doc_claims.py, test_generate_repo_badges.py all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u * chore(ast): delete extract_calls_generic — its own direct unit test was the last caller (#249 review) Review finding on PR #270: removing the dead `calls` tuple element from every per-language extractor (previous commit in this branch) stranded extract_calls_generic (mcp_server/core/ast_extractors.py) — nothing in production called it anymore, only TestCallExtraction's two tests called it directly. Dead code per coding-standards.md §9: deleted the function and its now-pointless direct unit test, corrected ast_extractors.py's module docstring (dropped the false "Also provides the generic call-site extractor used by all languages" claim), and updated ast_extractor_registry.py's _make_extractor docstring to record that the function itself, not just its result, is now gone. _MAX_CALL_NAME_LEN and _walk_type remain live — both are still used by the per-function, caller-qualified call extraction a few lines down (extract_calls_per_function), which is the value parse_file_ast actually consumes. Suite count recomputed from a live `pytest --collect-only -q` on this tree (6571, down 2 from 6573 for the deleted tests) and resynced across CLAUDE.md/README.md/CONTRIBUTING.md/.bestpractices.json/ docs/ASSURANCE-CASE.md/assets/badge-tests.svg; scripts/check_doc_claims.py --test-count 6571 and scripts/generate_repo_badges.py --check --test-count 6571 both green. Verification: tests_py/core/test_ast_parser.py + test_ast_extractors_multilang.py + test_ast_extractors.py + test_ast_parser_language_contract.py (69 passed), tests_py/core/ (3384 passed), full suite (6571 passed, 117 subtests passed, exact match to the recomputed collect-only count), ruff check + ruff format --check clean on all three changed .py files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u * refactor(ast): split extract_calls_per_function's nested walk under the 40-line/method cap (boy-scout, CLAUDE.md §4) Seen while touching this file for the extract_calls_generic deletion: extract_calls_per_function (51 lines including its nested `walk` closure) already violated this repo's 40-line/method cap on main, pre-existing and unrelated to either fix in this branch. Fixed in place since it is squarely in this diff's blast radius (coding-standards.md §14.1) and the split carries zero external risk — the public `extract_calls_per_function(root, source) -> dict[str, list[str]]` signature and behavior are unchanged, only the internals move. Split into three functions, each under the cap: `extract_calls_per_function` (17 lines, unchanged behavior/contract) delegates to `_walk_for_calls` (36 lines, the former nested closure — now a named top-level helper taking `out` as an explicit accumulator parameter instead of a captured free variable) which delegates per-function call collection to `_collect_call_basenames` (10 lines, the former inner per-call-type loop). Pure extract-method refactor: no branch, condition, or dedup logic changed. Not addressed here: this file's own line count (330 after this commit, still over the repo's 300-line/file cap — it was already 337 before either commit in this branch touched it). A real fix needs a genuine module split (this file has 6 importers: ast_parser.py plus the clike/extra/jvm/scripting extractor siblings and its own test file — Move-7's >5-importers High-stakes trigger), which means updating import statements in 5 other files. That is a separate, dedicated behavior-preserving-refactor PR per coding-standards.md §15.1 ("never ship the connection half-made... never mix the enabling refactor and the feature in one PR"), not a rider on a rebase-plus-one-review-finding PR already carrying real risk from two prior CONFLICTING states. Flagged in this session's report for the coordinator rather than filed as an issue (explicit instruction: no new issues this round). Verification: tests_py/core/test_ast_parser.py + test_ast_extractors_multilang.py + test_ast_extractors.py + test_ast_parser_language_contract.py (69 passed, unchanged), tests_py/core/ (3384 passed, unchanged), ruff check + ruff format --check clean, pyright 0 errors/0 warnings on the changed file. Full suite count unchanged at 6571 (no tests added or removed by this commit). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u * chore: rebase onto main and resync the advertised suite size to 6777 Rebase bookkeeping only. Conflicts were confined to the six files that hard-code the suite size, and only hunks differing solely by that number were resolved (main's figure wins); any hunk with real content divergence aborts the rebase for human/agent resolution instead of being guessed at. Count is the measured absolute from a real collection. The branch's own touched tests were re-run after resolution to catch content loss. * fix(ast): rebase reconciliation — retire an obsolete mutation-guard test, split test_ast_parser.py under the 300-line cap, dedupe a CHANGELOG entry Rebasing onto main pulled in #269/#286, which added test_make_extractor_threads_the_real_source_into_calls_extraction to pin mutmut mutant x__make_extractor__mutmut_10 (a source-argument mutation on the extract_calls_generic call _make_extractor used to thread through). This branch's own earlier commit deletes that exact call site and extract_calls_generic itself (dead code, issue #249 boy-scout pass), so the mutant the test existed to kill can no longer be generated from the source at all -- git's line-based merge didn't conflict (the two edits touch different lines), but the merged tree failed test_ast_extractors_multilang.py with AttributeError on ast_extractor_registry.extract_calls_generic. Root cause: a semantic merge conflict between two independently-correct changes, invisible to textual diffing. Fixed by deleting the retired test (its own in-file note records why) rather than adapting it -- the 2-tuple _make_extractor now composes exactly two calls, and a source-argument mutant on either is already killed by the 7 per-language tests (test_java et al.), each of which asserts on _imports/_defs output that is only correct when the REAL source bytes reach imports_fn/defs_fn. Verified empirically, not just by inspection: a scoped mutmut run (ast_extractor_registry.py + ast_parser.py, full 6-file test selection) reports 50 "survived" mutants recovered by mutation_recheck_survivors.py's full-selection reverification (the same eager-import-dispatch-table false-positive pattern #269 diagnosed) -- 0 genuine survivors. Boy-scout (seen while touching this tree): test_ast_parser.py had grown to 405 lines under this branch's own Go/Swift/Rust wrapper-test additions, over CLAUDE.md's 300-line/file cap -- a NEW violation this branch introduces (main's version is 171 lines), not pre-existing debt. Split (Extract Module, zero behavior change, same test names/assertions) into test_ast_parser.py (223 lines), test_ast_parser_node_extraction.py (103 lines: the fake-Node _extract_module_doc/_node_text unit tests) and test_ast_parser_extra_langs.py (122 lines: the Go/Swift/Rust parse_file_ast integration tests) -- all three under cap. Also seen: CHANGELOG.md's [Unreleased] section carried the #249 boy-scout entry TWICE (a stale pre-extract_calls_generic-deletion draft alongside the final one), both stating a suite count already stale relative to main's independent growth. Deduped to the single correct entry; reworded its count claim to a relative delta (net +20 tests) per the moment any other PR merges first -- assets/badge-tests.svg is the one artifact that still states one). Suite count re-measured post-fix: 6776 (pytest --collect-only -q on this tree), matching the full run (6771 passed + 5 skipped). assets/badge-tests.svg regenerated via scripts/generate_repo_badges.py --test-count 6776; scripts/check_doc_claims.py --test-count 6776 and scripts/generate_repo_badges.py --check --test-count 6776 both green. Verification: tests_py/core/test_ast_parser.py + test_ast_parser_node_extraction.py + test_ast_parser_extra_langs.py + test_ast_extractors.py + test_ast_extractors_multilang.py + test_ast_parser_language_contract.py (69 passed, unchanged assertions), tests_py/core/ (3384 passed), full suite (6771 passed, 5 skipped, 116 subtests passed), ruff check + ruff format --check clean on all changed files, pyright 0 errors/0 warnings on all changed files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #253. Also closes #249 and #251 — three filings of the same defect, and
this change meets all three acceptance-criteria lists (reconciliation below).
The defect, and what was under it
The pyright gate is zero-diagnostic, but its input was not pinned. CI
installs the hash-pinned export of
uv.lock; the documented developer installresolved
pyproject.toml'stree-sitter-language-pack>=0.24.0,<1.14. The twolanded seven minor versions apart, so the same commit reported 1 error to a
contributor and 0 to CI.
Probing every published wheel to find where the type surface actually changes
turned up a live runtime defect the old floor admitted:
SupportedLanguageget_parser→parse(b"...")csharpSupportedLanguageparamstrparambuiltins.ParserTypeError: 'bytes' object is not an instance of 'str'tree_sitter.Parser_get_extractor_and_treecallsget_parser(language).parse(content)withcontent: bytesand catches onlyImportError, so on any 1.9.0–1.12.2resolution
codebase_analyze/seed_projectdie with an uncaughtTypeErroron the first file. The pin's own comment described that window ("1.7.0+ …
AttributeErroron instances ofbuiltins.Parser") while the pin it annotatedsaid
<1.14and admitted it.What changed
pyproject.toml— floor>=0.24.0→>=1.12.5, with the measuredper-release table above as a
# source:comment. (>=0.24.0never matcheda 0.x release anyway: the pack goes 0.13.0 → 1.0.0.)
uv lockre-run withthe CI-pinned uv 0.11.3; resolution unchanged at 1.13.5, so no requirements
file moved.
mcp_server/core/ast_parser.py—_EXTRACTORSis keyed by the pack'sSupportedLanguageliteral (so the checker validates every grammar nameagainst the pack the environment resolved),
AST_SUPPORTEDis derivedfrom that table instead of restated beside it, and
_is_ast_languageis aTypeGuardthat narrows beforeget_parser. Deriving the set deleted anunreachable arm:
AST_SUPPORTED == set(_EXTRACTORS)held exactly, soif not extractor: return Nonecould never fire (§9), and_EXTRACTORS[language]is now total after the guard.CONTRIBUTING.md/CLAUDE.md— both documented installs resolve fromuv.lock(uv sync), not from the ranges, plus a newReproducing the pyright gate locally section with the exact environment CI
builds.
.github/workflows/ci.yml— the Type Check job prints the three packageversions its verdict depends on, one
grepper package so a missing onefails the step instead of degrading to
Unknowninside pyright.Completion Ledger (§13.2)
Paths introduced by the diff
git diff origin/main...HEAD_is_ast_languagereturns True (name inAST_SUPPORTED)test_is_ast_language_accepts_every_declared_name_is_ast_languagereturns Falsetest_is_ast_language_rejects_names_outside_the_set("",cobol,Python,text)_get_extractor_and_treeearly return on unsupported languagetest_unsupported_language_falls_back_before_touching_the_pack_get_extractor_and_treeexcept ImportError → Nonetest_missing_pack_falls_back_instead_of_raising(sys.modules[...] = None)_get_extractor_and_treesuccess →(_EXTRACTORS[language], tree)test_supported_language_with_an_extractor_parses_EXTRACTORS[language]cannotKeyErrortest_supported_set_is_exactly_the_extractor_table(equality, not inclusion)AST_SUPPORTEDnames are real for the installed packtest_every_supported_name_is_typed_by_the_installed_pack(get_args) +test_every_supported_name_parses_bytesis_available()True / False arms (touched file, previously unasserted)test_is_available_is_true_when_the_pack_imports/test_is_available_is_false_when_the_pack_is_absentbuild_extra_extractorsreturn-type changetest_every_supported_name_parses_bytesexercises all 7 of its keys; pyright validates them against the literaluv syncextras ==ci-typecheck.txtsettest_extras_match_the_ci_typecheck_constraint_settypecheck-tool.txtsettest_groups_match_the_typecheck_tool_constraint_set--no-default-groupson both sidestest_default_groups_are_excluded_like_the_exporttest_pyright_is_run_from_that_environmenttest_range_resolution_is_documented_as_forbiddentest_job_installs_the_hash_pinned_files--no-depstest_job_installs_the_project_without_resolving_its_rangestest_job_does_not_pip_install_an_extra_rangetest_job_records_the_resolved_type_surfaceNegative controls run for the parity gate (each edit reverted after):
removing
--extra otelfrom the documented block →test_extras_match_…fails; replacing ci.yml's
--no-deps -e .withpip install -e ".[dev,…]"→test_job_installs_the_project_without_resolving_its_rangesandtest_job_does_not_pip_install_an_extra_rangefail.§13.1 checklist
narrowing input — empty string, unknown name, wrong case, non-language text.
Nonereturns and the
ImportErrordegraded mode have tests; there is no signalemission on these paths (the fallback to the regex parser is the contract).
detect_languageoutput is narrowedbefore it reaches the pack; nothing else crosses.
.get; no new loops. C2 no resources. C3 no hot path measuredbecause none moved.
name was already derived from a path).
public
AST_SUPPORTEDkeeps its value and membership semantics.E2 consumers:
tests_py/core/test_ast_extractors_multilang.py(importsAST_SUPPORTED) andparse_file_ast— both pass. E3 N/A: no persistedformat. E4 the pin covers macOS/Linux/Windows wheels for cp310-abi3.
mode (regex fallback) is unchanged and asserted.
a version the old pin admitted — under
tree-sitter-language-pack 1.12.2,test_every_supported_name_parses_bytesandtest_supported_language_with_an_extractor_parsesfail with theTypeError.G3 deterministic: no temp paths, no sleeps,
monkeypatchscoped.G4 negative assertions present (
_is_ast_languageFalse arm,test_job_does_not_pip_install_an_extra_range). G5 below.ast_parser.pyis 267lines, every function under 50, core still imports only core+shared, the new
pack import is
TYPE_CHECKING-only. H2 the unreachable arm is gone.H4 CHANGELOG entry added. H5 one logic commit.
H7 boy-scout: below.
Evidence (commands + output)
Paired control, same tree, versions the new pin excludes:
Runtime probe of the same window:
Suite, lint, and the repo's own gates (G5):
Mutation testing (§12), scoped to the changed file:
All 8 mutants of the changed functions (
is_available,_is_ast_language,_get_extractor_and_tree) were killed; the 163 the run reports as "no tests"belong to
parse_file_ast(57),_extract_module_doc(37) and the per-languageextractors, which this scoped selection does not import — untouched by this
diff and covered by other test files (§12.1 scopes the blocking tier to the
changed lines).
Boy-scout (§14) — H7
ci.yml(SC2015×3 —A && B || Cretry loops rewritten asif …; then break; fi;SC2034— unused loop variable).actionlint .github/workflows/ci.ymlnow reports nothing; it reported 4 before.<1.7while pinning<1.14)and the
>=0.24.0floor that matches no 0.x release of this package.DeprecationWarnings for the stdlib sqlitedatetimeadapter atmcp_server/infrastructure/sqlite_compat.py:287. Fixing it changes theon-disk datetime spelling (§13.1 E3) and needs its own migration decision;
filed as sqlite_compat.py relies on the deprecated stdlib datetime adapter (3 DeprecationWarnings in the suite) #260, outside this change's blast radius (no import path from
core/ast_parser.py).Reconciliation with #249 and #251
_is_ast_languageTypeGuard+frozenset[SupportedLanguage]; nocast, notype: ignorerequirements/ci-typecheck.txt(exported fromuv.lock,--require-hashes, gated bygenerate_pip_constraints.py --check); the new Record the resolved type surface step prints ituv.lock--require-hashes+ the--checkLint step; the version probe fails on a missing packageuv sync+ pyright reports 0CI on the pushed tree (H6)
Run 30477779566 —
all 18 required checks pass (Lint, Type Check, Build Package, Test 3.10/3.11/
3.12/3.13, Test SQLite, Test Windows-SQLite, 3× CodeQL Analyze, 2× Docker
Build, Docker Smoke, 2× Fuzz PR batch). The Type Check job's new step and its
verdict:
That is the same
tree-sitter-language-packa lock-derived developer installnow resolves — which is the whole point of #253.