Skip to content

typecheck gate is resolver-dependent: pyright reports 1 error under uv.lock, 0 under CI's pip resolution (ast_parser.py:88) #249

Description

@cdeust

The typecheck CI job (.github/workflows/ci.yml:445) is a zero-diagnostic
gate on a pinned pyright (1.1.410, ci.yml:437), but the environment it types
against is not pinned: the job builds .venv with
pip install -e ".[dev,postgresql,sqlite,codebase,otel]" flashrank, which
resolves tree-sitter-language-pack freely inside >=0.24.0,<1.14
(pyproject.toml:77). The repo's own uv.lock pins a different version, and
the two resolutions disagree about whether the tree is clean.

Reproduction (2026-07-29, macOS 15.5, worktree at f606c37)

$ uv sync --all-extras
$ uv run --no-sync python -c "import importlib.metadata as m; print(m.version('tree-sitter-language-pack'))"
1.6.2
$ uv pip install pyright==1.1.410
$ .venv/bin/python -m pyright mcp_server/
mcp_server/core/ast_parser.py
  mcp_server/core/ast_parser.py:88:23 - error: Argument of type "str" cannot be
  assigned to parameter "name" of type "SupportedLanguage" in function "get_parser"
    Type "str" is not assignable to type "SupportedLanguage"
      "str" is not assignable to type "Literal['actionscript']"
      ... (reportArgumentType)
1 error, 0 warnings, 0 informations

CI reports 0 on the same commit (gh run list --branch main --workflow ci.yml:
f606c37success), because pip resolves the newest allowed release —
1.13.5 today — whose get_parser signature differs from 1.6.2's.

Root cause

mcp_server/core/ast_parser.py:88 calls get_parser(language) with a plain
str. The value IS narrowed at runtime (if language not in AST_SUPPORTED: return None, line 78), but membership in a set[str] does not narrow str to
the SupportedLanguage Literal union for a type checker. Under the older
stub the call is an error; under the newer one it is not. So the gate's verdict
is a property of the resolver, not of the source.

This is the "unpinned linter is not a gate" failure mode: a green
zero-diagnostic job that would go red (or hide a real diagnostic) on a
dependency bump nobody reviewed.

Acceptance criteria

  1. mcp_server/core/ast_parser.py:88 type-checks clean under both
    resolutions — narrow language to the checker's satisfaction at the
    AST_SUPPORTED guard (e.g. make AST_SUPPORTED a frozenset of the
    Literal members and have _get_extractor_and_tree return early on a
    TypeGuard), rather than casting the error away.
  2. The typecheck job installs a reproducible environment — the same
    resolution the repo commits (uv sync --locked) — so the gate measures the
    source, not the resolver. Quote the installed
    tree-sitter-language-pack version in the job log.
  3. python -m pyright mcp_server/ reports 0 errors under both the uv.lock
    resolution and a fresh unconstrained pip resolution; both runs quoted in the
    PR.
  4. A regression guard: the typecheck job fails loudly if the resolved
    environment differs from uv.lock (that is what uv sync --locked gives).

Found during #233 (synaptic-plasticity import-cycle fix); the diagnostic is in
an unrelated file that #233 does not touch, so it is deferred here per the
boy-scout rule rather than folded into that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions