)]}'
{
  "log": [
    {
      "commit": "da9e283158c52d1906e6b925e82cbfc10ebade52",
      "tree": "68277a397becdde25d1c9778ed4d2e1d2305f3aa",
      "parents": [
        "c9decc654f408ff6071f2fe13178ce633d4af605"
      ],
      "author": {
        "name": "William Woodruff",
        "email": "william@astral.sh",
        "time": "Mon Aug 31 14:19:43 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 14:19:43 2026 -0400"
      },
      "message": "Swap `cargo shear` and `hyperfine` to the Astral toolchain (#28197)\n\nSigned-off-by: William Woodruff \u003cwilliam@yossarian.net\u003e"
    },
    {
      "commit": "c9decc654f408ff6071f2fe13178ce633d4af605",
      "tree": "97bedc9f057ec67afba2f69bb15f4a88db24d23d",
      "parents": [
        "94181e44ceced7c21cf2ebb761e5b732714914da"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Aug 31 10:27:13 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 10:27:13 2026 -0700"
      },
      "message": "[ty] Preserve recursion guards across nested type operations (#28150)\n\nOn main we still stack overflow when `type(value)` or `value.__class__`\ntraverses recursive aliases containing `type[T]`, or type variables\nwhose recursive bounds or constraints are hidden behind aliases.\nSpecialization and materialization can start nested meta-type\nprojections that lose the original projection\u0027s recursion guards.\n\nThis change carries a shared `TypeRecursionContext` through those\noperations while keeping each transformation\u0027s result cache separate.\nAlias expansions that depend on active guards stay out of the\nmaterialization cache. Direct alias recursion retains its precise\nfallback, while re-entering meta-type projection conservatively accounts\nfor possible metaclasses.\n\nThe `TypeRecursionContext` is currently used only for `to_meta_type`\ncycles, but it is intentionally named and structured in such a way that\nwe could add more recursion-trackers to it and start to unify our\nrecursion tracking a bit more, so we can handle more cross-method\nrecursion cases.\n\nFollow-up to #28143.\n\n## Test plan\n\nAdds mdtests for recursive class aliases with growing and nested\nspecializations, aliased recursive type-variable bounds and constraints,\nand repeated `type()`/`__class__` queries. Materialized recursive-alias\ntests check that upper, lower, and ordinary alias types retain\nindependent results across interleaved queries."
    },
    {
      "commit": "94181e44ceced7c21cf2ebb761e5b732714914da",
      "tree": "a2f3f380d8f1e46495a94eb849f0518f1265140b",
      "parents": [
        "a8d5ba53d1378873d75fb46794452dedfd6ac45b"
      ],
      "author": {
        "name": "Ali Hamdan",
        "email": "ali.hamdan.dev@gmail.com",
        "time": "Mon Aug 31 17:46:20 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 15:46:20 2026 +0000"
      },
      "message": "[ty] Add regression test for ty#3598 (#28154)\n\n## Summary\n\nResolves\nhttps://github.com/astral-sh/ty/issues/3598#issuecomment-5247040005\n\n---------\n\nCo-authored-by: Carl Meyer \u003ccarl@astral.sh\u003e"
    },
    {
      "commit": "a8d5ba53d1378873d75fb46794452dedfd6ac45b",
      "tree": "76d8d24a52faa1a0cbcd58fe9e5e2e0200713cb7",
      "parents": [
        "5c64006c01f1b6289d18dd25b0d4ec5f95361424"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Aug 31 08:38:29 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 08:38:29 2026 -0700"
      },
      "message": "[ty] Share call-unwrapping fix generation with Ruff (#28145)\n\nIn #28092 I fixed a bug where ty\u0027s redundant-cast removal fix could\ncreate broken code if the original `cast()` call was multi-line. Doug\ncommented that this could perhaps be a general-purpose utility -- and it\nturned out there were two existing similar Ruff rules with pre-existing\nmulti-line support that could benefit from such a shared utility.\n\nThis PR introduces a shared utility in `ruff_python_edits` for removing\na function call in favor of one of its arguments. This is the common\nshape between redundant-cast removal, redundant `int()` removal, and\nredundant `round()` removal.\n\nThe new utility is more robust than the previous code for any of the\nthree cases. Previously removing redundant `int`, `round`, and\n`typing.cast` calls could discard parentheses needed for line\ncontinuation or for token separation. For example, `int(1).to_bytes()`\ncould become `1.to_bytes()`, which is invalid.\n\nThe shared helper preserves optional argument parentheses and the\ncomments inside them, adds grouping where required, and avoids redundant\nparentheses when the surrounding syntax already allows the expression.\nRuff and ty retain their own diagnostic eligibility and fix-safety\ndecisions.\n\n## Test plan\n\n- Extend RUF046 and RUF057 fixtures for split callees, assignment\nexpressions, integer attributes, adjacent keywords, and comments inside\noptional parentheses. Existing snapshot cases retain their fix text and\nsafety classifications.\n- Add compact helper tests in `ruff_python_edits` for conditional\ncallees, comprehension iterables, and dictionary expressions inside\nf-strings.\n- Existing ty cast mdtests cover positional and keyword arguments,\noperator precedence, multiline values, and comment preservation."
    },
    {
      "commit": "5c64006c01f1b6289d18dd25b0d4ec5f95361424",
      "tree": "5ca23839ce5de7495ec87e9f184c4c2530d76ae8",
      "parents": [
        "fd9e79688ff6303e284779e72ded799bc73fa50a"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Aug 31 08:34:03 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 15:34:03 2026 +0000"
      },
      "message": "[ty] Preserve generic receivers in implicit __init_subclass__ calls (#28138)\n\nA generic subclass with a defaulted type parameter can incorrectly fail\nthe implicit `__init_subclass__` receiver check. The receiver is\ndefault-specialized while the inherited method\u0027s `Self` bound retains\nthe subclass\u0027s type variables, producing a comparison such as\n`Child[int]` against `Base[T@Child]`.\n\nUse the subclass\u0027s identity specialization for the implicit `cls`\nargument so the receiver and inherited hook use the same type\nparameters. This accepts valid class definitions while retaining\nexplicit receiver restrictions and keyword argument validation.\n\nCloses astral-sh/ty#4411.\n\n## Test plan\n\nAdded mdtests covering:\n\n- Generic subclass receivers with no defaults, all-default parameters,\nand mixed required and defaulted parameters.\n- Legacy `TypeVar` defaults and explicit `cls` annotations, including\nrejection of incompatible receiver specializations.\n- Class keyword arguments, including values typed using a subclass\nparameter and rejection of incompatible values."
    },
    {
      "commit": "fd9e79688ff6303e284779e72ded799bc73fa50a",
      "tree": "9ba2cf1e6a04fe4f3a60ac4e0d465076859a465c",
      "parents": [
        "976a005266d84fb9da3996d83e136738474671d9"
      ],
      "author": {
        "name": "Pierre Sassoulas",
        "email": "pierre.sassoulas@gmail.com",
        "time": "Mon Aug 31 16:32:03 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 10:32:03 2026 -0400"
      },
      "message": "[`flake8-pytest-style`] Add an autofix for `PT020` (#27993)\n\n## Summary\n\nFollowing discussion in https://github.com/pytest-dev/pytest/pull/14755.\nwhere we consider deleting the function in pytest 10. It\u0027s better if an\nautofix exists. I marked it as safe becuase ``fixture`` is equivalent\nafaiu. (Probably better to wait for a decision in the other issue to\ndocument it in this PR if it\u0027s removed in pytest 10)\n\n## Test Plan\n\nTook example with NPY003\u0027s implementation, tested alias for\nyield_fixture and pytest, and various imports, used bare, with parens,\nwith args, with trailing comment, and inside a class."
    },
    {
      "commit": "976a005266d84fb9da3996d83e136738474671d9",
      "tree": "7675999a9e7fd401554e048f45691c1014cf1874",
      "parents": [
        "79e32050ddaa39c3e7588e20b8fbd8baf8e84c1a"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Mon Aug 31 15:17:47 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 09:17:47 2026 -0400"
      },
      "message": "[ty] Fix auto-completion panic involving bare declarations (#28191)\n\n## Summary\n\nRequesting completion after `val` in `from module import val` previously\npanicked when `module.py` contained a declaration without a right hand\nside value, even for an unrelated name:\n\n```python\ndeclared: int\nvalue \u003d 1\n```\n\ncloses https://github.com/astral-sh/ty/issues/4423.\n\n## Test Plan\n\n- Three regression tests for the original bug and related issues"
    },
    {
      "commit": "79e32050ddaa39c3e7588e20b8fbd8baf8e84c1a",
      "tree": "dcc20958c085edfeedf63791c6a8ebaed4f905ea",
      "parents": [
        "942e781b9684577c62b3aec017cbb29dd2ea36eb"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Mon Aug 31 08:49:34 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 08:49:34 2026 +0100"
      },
      "message": "[ty] Expand tests for upcoming redundant-condition rules (#28182)\n\n## Summary\n\nBuilds on #28032.\n\nExpand the test suite introduced in #28030 for the upcoming\n`redundant-condition` and `redundant-condition-strict` rules in #28034.\nThe new examples cover short-circuiting, chained comparisons,\nconditional expressions, nested boolean tests, required `TypedDict` keys\nestablished by narrowing, and exemptions for environment-dependent\nvalues and defensive runtime checks.\n\nOnly `redundant_condition.md` changes. Expected diagnostics remain\nmarked with TODOs, while `reveal_type` assertions document the\ndistinction between an expression\u0027s inferred value type and its\ntruthiness when used directly as a condition."
    },
    {
      "commit": "942e781b9684577c62b3aec017cbb29dd2ea36eb",
      "tree": "b066d16c6e8988c31e927b6bb1fa78779693ab2f",
      "parents": [
        "4d622392df65335245f0267e3b6465a7e992dc4c"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Mon Aug 31 07:24:21 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Aug 31 07:24:21 2026 +0100"
      },
      "message": "[ty] Share definition resolution between type inference and IDE features (#28032)\n\n## Summary\n\nThis is a behaviour-preserving refactor of definition resolution:\nnavigation targets, docstrings, and existing type-checking results are\npreserved. The changes are to the API boundaries and the inference work\nperformed internally.\n\n## LSP benefits\n\nGo-to-definition and go-to-declaration for ordinary builtins such as\n`isinstance` no longer request completed inference of the enclosing\nscope just to check whether numeric-annotation expansion applies. Only\n`float` and `complex` need that check. Completing scope inference\ncomputes and caches types for the scope\u0027s expressions, along with\ndiagnostics and other inference data, even though navigation only needs\nthe symbol\u0027s definition.\n\nThe expected benefit is lower navigation latency when scope inference is\nuncached or has been invalidated by an edit, plus fewer temporary\nallocations and fewer inference results retained solely for navigation.\nThe savings depend on the workload: cached inference leaves little\ncomputation to avoid, and diagnostics may still require the same cached\ndata. This does not guarantee lower steady-state memory usage for the\nserver.\n\n## Reuse during type inference\n\nThe shared API will support inference-side consumers such as\n[#27634](https://github.com/astral-sh/ruff/pull/27634), whose\nredundant-condition analysis needs source definitions as well as\ninferred types:\n\n- It follows assignments and imports to recognize conditions derived\nfrom `sys.version_info`, `sys.platform`, `os.name`, or\n`typing.TYPE_CHECKING`. These can be constant for the configured target\nwhile still being deliberate compatibility guards, including when\naccessed through aliases or attributes.\n- It finds the annotation behind an always-truthy tuple so the\ndiagnostic can point to `tuple[T]` and suggest `tuple[T, ...]` when a\ntuple of arbitrary length may have been intended.\n\nThese checks run while the enclosing scope is still being inferred.\nUsing the IDE API to obtain expression types at that point can re-enter\ninference of the same scope, causing avoidable Salsa query cycles and\nrepeated work. The shared helpers instead accept an explicit scope or an\nalready-inferred receiver type, allowing these checks to reuse the\nexisting name, import, and member-resolution rules without requesting\ncompleted inference of the current scope. Pydantic\u0027s existing\nannotation-alias lookup is also migrated to this API.\n\n## Diff overview\n\n- **Moved:** Existing scope traversal, class/MRO member lookup, import\nresolution, and resolved-definition/docstring helpers move into\n`definition_resolution`, preserving their lookup algorithms.\nNumeric-annotation expansion and stub-to-implementation mapping remain\nin `ide_support`.\n- **Added:** Shared lookup entry points and three regression tests that\nassert definition lookup does not request completed scope inference.\n- **Changed:** IDE wrappers and the Pydantic caller delegate to the\nshared helpers; builtin name lookup gets the `float`/`complex` inference\nguard described above. The remaining edits adapt parameters, imports,\nand visibility, and document the shared API\u0027s contracts."
    },
    {
      "commit": "4d622392df65335245f0267e3b6465a7e992dc4c",
      "tree": "885519fa2a64ffd58b9d311f3882b1180f707f3f",
      "parents": [
        "68be8be711a10e282cbd8b29b382d96fdce675c4"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat Aug 29 17:56:34 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 17:56:34 2026 -0400"
      },
      "message": "[ty] Avoid cubic narrowing with repeated assignments and context managers (#27787)\n\n## Summary\n\nRepeated conditional assignments make us reevaluate the same narrowing\nconstraints. Suppressing context managers add more paths and make this\nworse:\n\n```python\nfrom contextlib import suppress\n\ndef may_raise(value: int) -\u003e int: ...\n\ndef f(value: int | str) -\u003e int | str:\n    if isinstance(value, int):\n        value \u003d may_raise(value)\n\n    with suppress(ValueError):\n        if isinstance(value, int):\n            value \u003d may_raise(value)\n\n    return value\n```\n\nWe now reuse one narrowing projector across bindings and cache shared\nconstraint suffixes in Salsa. Reachability caching also covers\ncontext-manager predicates.\n\nThe cache uses the existing projected-graph evaluator. When a join needs\nto inspect a cached suffix\u0027s predicates, we expand it so complementary\nbranches can cancel and `TypeGuard` narrowing doesn\u0027t leak past the\njoin.\n\nMedians from six runs per version on Linux, with matching builds and\ninputs. The two versions were measured separately:\n\n| Workload | `main` | This PR | Speedup |\n| --- | ---: | ---: | ---: |\n| 320 suppressing context managers | 1.137 s | 0.352 s | 3.2× |\n| 160 suppressing context managers interleaved with calls | 0.687 s |\n0.272 s | 2.5× |\n| 320 conditional assignments | 9.666 s | 0.922 s | 10.5× |\n| 320 conditional assignments inside suppressing context managers |\n11.531 s | 1.357 s | 8.5× |\n\nFollow-up to #27219."
    },
    {
      "commit": "68be8be711a10e282cbd8b29b382d96fdce675c4",
      "tree": "972719bfa5dd123addb220ca79fe58f41d7fc680",
      "parents": [
        "4f6426ef83d14642ebbb9fb069a95d32a0fa2684"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat Aug 29 15:26:13 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 15:26:13 2026 -0400"
      },
      "message": "[ty] Share class member source-method recovery (#28160)\n\n## Summary\n\nMove local source-method recovery from override checking into `Member`\nin `list_members.rs`, so class-level checks can share it. We retain\nfunctions represented by the member type, including property accessors,\nand recover local definitions when a decorator replaces the function\u0027s\ntype.\n\nOverride checking still selects the same definitions and diagnostic\nlocations. This is a refactor with no behavior changes; deleted-method\nhandling remains unchanged."
    },
    {
      "commit": "4f6426ef83d14642ebbb9fb069a95d32a0fa2684",
      "tree": "7a76684d9ea4a4521884dba7e83b2d0cd6f9f1da",
      "parents": [
        "2c3fa3e32f31cbb594f289e3a3e1bdc40de33262"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat Aug 29 15:13:23 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 15:13:23 2026 -0400"
      },
      "message": "[ty] Extract descriptor setter inference (#28159)\n\n## Summary\n\nMove descriptor setter-domain inference from `protocol_class.rs` to\n`attribute_write.rs`, alongside the other shared attribute-write\nhelpers. This makes the existing inference available to declared\nvariance checking in #28155.\n\nThe helper bodies and protocol behavior are unchanged."
    },
    {
      "commit": "2c3fa3e32f31cbb594f289e3a3e1bdc40de33262",
      "tree": "66c36635eef7ff0f74ffaddefd0fb33ef7f9221c",
      "parents": [
        "03ade086b1faff4efc079d5ed3ebd28455cec484"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Sat Aug 29 19:06:44 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 19:06:44 2026 +0100"
      },
      "message": "[ty] Clarify the scope of `unsound-assignment` (#28157)"
    },
    {
      "commit": "03ade086b1faff4efc079d5ed3ebd28455cec484",
      "tree": "1476c4aab787c7f798e9a399fa8ec7922c258901",
      "parents": [
        "917c6269546b5293cb9c873754f1531e800d842e"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat Aug 29 13:43:41 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 13:43:41 2026 -0400"
      },
      "message": "[ty] Validate declared variance in method signatures (#28116)\n\n## Summary\n\nPreviously, we used declared variance when comparing generic class\nspecializations, but didn\u0027t check whether the class\u0027s methods respected\nit. We now report `invalid-generic-class` when a method uses a\nclass-scoped type parameter in a position that conflicts with its\ndeclared variance.\n\n```python\nfrom typing import Generic, TypeVar\n\nT_co \u003d TypeVar(\"T_co\", covariant\u003dTrue)\n\nclass Source(Generic[T_co]):\n    def get(self) -\u003e T_co:\n        raise NotImplementedError\n\n    # Before: no error. After: invalid-generic-class.\n    def set(self, value: T_co) -\u003e None: ...\n```\n\nWe reuse the existing variance analysis for `TypeVar`, `ParamSpec`, and\n`TypeVarTuple`, including variance reversal through `Callable`.\nConstructors remain exempt, and protocols continue through their\nseparate interface checks.\n\nExplicit receivers equivalent to `Self` or the class\u0027s own\nspecialization, such as `C[T]`, are checked as well, including the\ncorresponding classmethod receivers. These annotations do not restrict\nwhich class specializations expose the method.\n\nFor now, we defer overloaded methods, methods with independent type\nparameters, and methods with restricted receivers. A broader overload\ncan cover an otherwise incompatible signature, a method\u0027s own type\nparameter can make an input redundant, and a specialized receiver can\nrestrict which class specializations expose the method. Checking these\nsignatures one occurrence at a time can therefore reject valid code.\nThese exclusions apply equally to `TypeVar`, `ParamSpec`, and\n`TypeVarTuple`; they also leave some invalid signatures unchecked.\n\nFor example, this generic default remains valid: `T` already accepts any\ndefault, and the return type includes both `T_co` and `T`.\n\n```python\nT \u003d TypeVar(\"T\")\n\nclass Mapping(Generic[T_co]):\n    # No variance error: the T_co arm in the parameter is redundant.\n    def get(self, default: T_co | T) -\u003e T_co | T:\n        return default\n```\n\nThis moves\n[`generics_paramspec_variance`](https://github.com/python/typing/blob/5672d88d54c149de13d0bbd3f2995b5449f72e20/conformance/tests/generics_paramspec_variance.py)\nand\n[`generics_typevartuple_variance`](https://github.com/python/typing/blob/5672d88d54c149de13d0bbd3f2995b5449f72e20/conformance/tests/generics_typevartuple_variance.py)\nfrom Partial to Pass.\n\n\u003cdetails\u003e\n\u003csummary\u003eCase analysis\u003c/summary\u003e\n\nMypy 2.1.0, pyright 1.1.410, and pyrefly 1.3.0-dev.1 also reject the\nordinary `TypeVar` misuse above. Those versions do not support explicit\nvariance on `ParamSpec` or `TypeVarTuple`: they report errors on valid\nvariance declarations, so they do not pass these conformance cases or\nprovide a direct comparison for the method checks below.\n\nReturning `Callable[P, None]` uses `P` contravariantly, while accepting\nthat callable as a parameter uses `P` covariantly. A covariant\n`ParamSpec` permits the latter but not the former:\n\n```python\nfrom typing import Callable, Generic, ParamSpec\n\n# ty: OK; mypy, pyright, pyrefly: error (unsupported variance argument).\nP_co \u003d ParamSpec(\"P_co\", covariant\u003dTrue)\n\nclass Callbacks(Generic[P_co]):\n    def accepts(self, callback: Callable[P_co, None]) -\u003e None: ...  # ty: OK\n\n    # ty before: no error. ty after: invalid-generic-class.\n    def returns(self) -\u003e Callable[P_co, None]:\n        raise NotImplementedError\n```\n\nFor `TypeVarTuple`, accepting unpacked arguments uses the type variable\ntuple contravariantly, while returning a tuple uses it covariantly. A\ncontravariant `TypeVarTuple` permits the former but not the latter:\n\n```python\nfrom typing import Generic\nfrom typing_extensions import TypeVarTuple\n\n# ty: OK; mypy, pyright, pyrefly: error (unsupported variance argument).\nTs_contra \u003d TypeVarTuple(\"Ts_contra\", contravariant\u003dTrue)\n\nclass Arguments(Generic[*Ts_contra]):\n    def accepts(self, *args: *Ts_contra) -\u003e None: ...  # ty: OK\n\n    # ty before: no error. ty after: invalid-generic-class.\n    def returns(self) -\u003e tuple[*Ts_contra]:\n        raise NotImplementedError\n```\n\n\n\u003c/details\u003e"
    },
    {
      "commit": "917c6269546b5293cb9c873754f1531e800d842e",
      "tree": "2bf2aeadb2cea22244b582f5d953c26731848c4e",
      "parents": [
        "59b6e6261ae0b750505d7dd48906b865bb5e4b24"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat Aug 29 08:44:34 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 12:44:34 2026 +0000"
      },
      "message": "[ty] Reject class access to generic instance attributes (#28123)\n\n## Summary\n\nPreviously, we allowed generic instance attributes to be read or written\nthrough the generic class itself, including through a specialized\ngeneric alias. We now report `invalid-attribute-access` for these\naccesses: specializing a class does not give it separate attribute\nstorage for each set of type arguments.\n\n```python\nclass Node[T]:\n    label: T\n\n    def __init__(self, label: T) -\u003e None:\n        self.label \u003d label\n\n# Before: no errors. After: invalid-attribute-access on each line.\nNode[int].label \u003d 1\nNode[int].label\nNode.label \u003d 1\nNode.label\n\nnode \u003d Node(1)\nnode.label \u003d 2  # Before and after: OK.\n```\n\nWe check the generic declaration before applying the class\u0027s type\narguments, using the existing variance analysis to detect dependencies\nthrough type aliases without expanding recursive specializations.\nInstance access, class variables, and attributes independent of the\nclass\u0027s type parameters remain valid. Access to methods and descriptors\nretains its existing behavior, and invalid accesses retain their\ninferred types for error recovery.\n\nImportantly, we continue to allow access through `cls`,\n`type(instance)`, and parameters annotated with `type[...]`. These\nreceivers can refer to concrete subclasses with their own class\nattributes. The typing specification [makes the analogous\n`type(instance).attribute` diagnostic\noptional](https://github.com/python/typing/commit/b41e5096bc9b1333355911614648616f7cfbf943)\nfor that reason.\n\nThis moves\n[`generics_type_erasure`](https://github.com/python/typing/blob/b41e5096bc9b1333355911614648616f7cfbf943/conformance/tests/generics_type_erasure.py)\nfrom Partial to Pass.\n\n\u003cdetails\u003e\n\u003csummary\u003eCase analysis\u003c/summary\u003e\n\nMypy 2.1.0, pyright 1.1.410, and pyrefly 1.3.0-dev.1 all reject the four\nclass accesses above. The Pyrefly comparisons use its `default` preset.\nPyright and pyrefly pass the full conformance file. Mypy still fails two\nseparate assertions because it infers `Never` rather than `Any` for an\nunspecialized constructor call.\n\nIn `__init_subclass__`, `cls` refers to the subclass being initialized.\nMkDocs reads its configuration metadata through this receiver, while\nsteam.py assigns the converter\u0027s target type. We allow both patterns, as\ndo mypy, pyright, and pyrefly:\n\n```python\nfrom typing import Generic, TypeVar\n\nT \u003d TypeVar(\"T\")\n\nclass BasePlugin(Generic[T]):\n    config_class: T\n\n    def __init_subclass__(cls):\n        cls.config_class  # All four: OK.\n\nclass Converter(Generic[T]):\n    converter_for: T\n\n    def __init_subclass__(cls, converter_for):\n        cls.converter_for \u003d converter_for  # All four: OK.\n```\n\nWe also allow reads through `cls` in `__new__` and classmethods.\nPrefect\u0027s worker configuration, Static-frame\u0027s store configuration, and\nSymPy\u0027s Gaussian domains use this pattern, with concrete subclasses\nsupplying the attributes. All four checkers accept these examples:\n\n```python\nfrom typing import Generic, TypeVar\n\nT \u003d TypeVar(\"T\")\n\nclass GaussianElement(Generic[T]):\n    base: T\n    _parent: T\n\n    def __new__(cls):\n        cls.base  # All four: OK.\n        return super().__new__(cls)\n\n    @classmethod\n    def _get_xy(cls):\n        cls._parent  # All four: OK.\n```\n\nFor class objects passed as parameters, we agree with mypy: the receiver\nmay be a concrete subclass, even when the annotation names a generic\nbase. Pyright and Pyrefly\u0027s `default` preset reject both examples below.\nPyrefly\u0027s `basic` preset accepts them because it disables the relevant\ncheck. The same distinction applies whether the class parameter retains\na type variable, as in pytest, or fixes its type argument, as in\nStatic-frame:\n\n```python\nfrom typing import Generic, TypeVar\n\nT \u003d TypeVar(\"T\")\n\nclass CaptureBase(Generic[T]):\n    EMPTY_BUFFER: T\n\ndef capture(captureclass: type[CaptureBase[T]]):\n    # ty, mypy: OK; pyright, pyrefly (default): error.\n    captureclass.EMPTY_BUFFER\n\nclass Store(Generic[T]):\n    _STORE_CONFIG_CLASS: T\n\ndef read(cls: type[Store[object]]):\n    # ty, mypy: OK; pyright, pyrefly (default): error.\n    cls._STORE_CONFIG_CLASS\n```\n\n\u003c/details\u003e"
    },
    {
      "commit": "59b6e6261ae0b750505d7dd48906b865bb5e4b24",
      "tree": "797e19094660a0af21dd1fe317ca02d09e77e481",
      "parents": [
        "700421c2ab58b99063d30eef79da653c52dca23c"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Sat Aug 29 12:57:25 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 11:57:25 2026 +0000"
      },
      "message": "[ty] Fix cyclic-alias expectations in type() tests (#28153)"
    },
    {
      "commit": "700421c2ab58b99063d30eef79da653c52dca23c",
      "tree": "ff2fc51acb2e8d6fe26a88a97073e0b3e3dfd61c",
      "parents": [
        "33648d47d770930cea3056e6e6bda4ba3e52e32a"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat Aug 29 06:57:56 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 06:57:56 2026 -0400"
      },
      "message": "[ty] Reject circular type alias definitions (#28140)\n\n## Summary\n\nPreviously, we accepted circular `TypeAliasType` definitions and aliases\nthat include themselves as union members. We now report\n`cyclic-type-alias-definition` for these cases, while continuing to\nallow recursion through containers and other concrete types.\n\n```python\nfrom typing import TypeAliasType\n\n# Before: no error. After: cyclic-type-alias-definition.\nDirect \u003d TypeAliasType(\"Direct\", \"Direct\")\n\n# Before: no error. After: cyclic-type-alias-definition.\ntype IntOr \u003d int | IntOr\n\n# Before and after: no error.\ntype Tree \u003d int | list[Tree]\n```\n\nWe follow references through aliases and unions, stopping when recursion\noccurs inside another type, such as `list[Tree]`. For generic aliases,\nwe follow only the type arguments exposed by the alias\u0027s definition.\nThis lets us distinguish an actual cycle from a finite expression like\n`Identity[Identity[int]]`, or an unused recursive argument like\n`Constant[Cycle]` when `Constant[T]` is defined as `int`.\n\nThe checks reuse cached summaries across alias definitions. Checking a\nchain of 8,192 aliases takes 0.092 seconds, compared with 0.087 seconds\non the base branch, using the median of five runs with matching debug\nbuilds.\n\nThis moves\n[`aliases_typealiastype`](https://github.com/python/typing/blob/cf943ccbea5596ef969eec6e2260097ff697b7ba/conformance/tests/aliases_typealiastype.py)\nand\n[`aliases_type_statement`](https://github.com/python/typing/blob/cf943ccbea5596ef969eec6e2260097ff697b7ba/conformance/tests/aliases_type_statement.py)\nfrom Partial to Pass, adding three missing errors in the former and one\nin the latter. The other 143 conformance results are unchanged.\n\n\u003cdetails\u003e\n\u003csummary\u003eCase analysis\u003c/summary\u003e\n\nMypy 2.1.0, pyright 1.1.410, and pyrefly 1.3.0-dev.1 (with the default\npreset) already report the four required errors that we were missing.\nAll three reject direct, mutual, and union cycles; the differences below\nconcern additional generic alias cases.\n\nChanging a recursive reference\u0027s type argument does not make a union\ncycle valid. All four checkers reject this definition, while accepting\nrecursion through a container:\n\n```python\n# ty, mypy, pyright, pyrefly: error.\ntype Cycle[T] \u003d T | Cycle[str]\n\n# ty, mypy, pyright, pyrefly: OK.\ntype Tree[T] \u003d T | list[Tree[T]]\n```\n\nRecursion through a container remains valid when the type arguments\ngrow. In `Repeated` below, expanding the aliases outside `list` reaches\n`int`, so there is no circular definition. Ty and pyright accept it;\npyrefly reports a cyclic self-reference. Mypy rejects the underlying\n`Recursive` definition because it does not support this pattern of\ngrowing type arguments, and also reports an error on `Repeated`.\n\n```python\n# ty, pyright, pyrefly: OK.\n# mypy: error; growing type arguments in recursive aliases are unsupported.\ntype Recursive[T] \u003d T | list[Recursive[list[T]]]\n\n# ty, pyright: OK. Mypy, pyrefly: error.\ntype Repeated \u003d Recursive[Recursive[int]]\n```\n\nIn contrast, substituting `Cycle` for `T` exposes `Cycle` itself as a\nunion member. Ty, pyright, and pyrefly reject this. Mypy rejects\n`Recursive` as above, so it cannot provide a useful comparison for this\nuse of the alias:\n\n```python\ntype Recursive[T] \u003d T | list[Recursive[list[T]]]\n\n# ty, pyright, pyrefly: error.\ntype Cycle \u003d Recursive[Cycle]\n```\n\nThese results also hold for equivalent definitions using\n`TypeAliasType`.\n\nA generic alias that returns its argument cannot make a circular\ndefinition valid. Ty and pyrefly reject this cycle, but pyright accepts\nit. Mypy did not finish checking the isolated example within 20 seconds:\n\n```python\ntype Identity[T] \u003d T\n\n# ty, mypy, pyright, pyrefly: OK.\ntype Repeated \u003d Identity[Identity[int]]\n\n# ty, pyrefly: error. Pyright: OK. Mypy: timed out.\ntype Cycle \u003d Identity[Cycle]\n```\n\n\u003c/details\u003e"
    },
    {
      "commit": "33648d47d770930cea3056e6e6bda4ba3e52e32a",
      "tree": "bc86dc02eb0eb64748e6d27220649cf9085c76b8",
      "parents": [
        "38eec9759a210b81e9d651b73cc24f2e91cb4e86"
      ],
      "author": {
        "name": "Zsolt Dollenstein",
        "email": "zsol.zsol@gmail.com",
        "time": "Sat Aug 29 10:59:52 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Aug 29 09:59:52 2026 +0000"
      },
      "message": "[ty] Check direct dependencies in PEP 723 scripts (#28136)\n\n`missing-direct-dependency` currently skips PEP 723 scripts. This\nextends the rule to scripts using uv integration, checking imports\nagainst the script\u0027s inline `dependencies` list and directing users\nthere when a declaration is missing.\n\nEach script uses its own dependency graph, independently of the\nworkspace and other scripts. The dependency metadata is cached\nseparately from script settings so diagnostics update after\nsynchronization even when those settings are unchanged. The rule skips\nscripts whose metadata is unavailable or out of date."
    },
    {
      "commit": "38eec9759a210b81e9d651b73cc24f2e91cb4e86",
      "tree": "a0abf081ecfceb7849851510ae90ff22fccb2754",
      "parents": [
        "92bb19801594c744da6c008c1235f49cc7b3b161"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Fri Aug 28 21:53:03 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 21:53:03 2026 -0700"
      },
      "message": "[ty] Add \"Find references\" support for pytest fixtures (#28075)\n\n\u003c!--\nThank you for contributing to Ruff/ty! To help us out with reviewing,\nplease consider the following:\n\n- Does this pull request include a summary of the change? (See below.)\n- Does this pull request include a descriptive title? (Please prefix\nwith `[ty]` for ty pull\n  requests.)\n- Does this pull request include references to any relevant issues?\n- Does this PR follow our AI policy\n(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?\n--\u003e\n\n## Summary\n\nThis adds \"Find References\" support for pytest fixtures. It connects\nfixtures to the parameters that request them in tests and other\nfixtures, to uses of those parameters, and to declarations and imports.\nThe reference search respects fixture overrides in classes, modules, and\napplicable `conftest.py` files, and includes fixtures from pytest\u0027s\ninstalled core plugins.\n\nFor example, given:\n\n```python\n# src/test_example.py\nimport pytest\n\n@pytest.fixture\ndef resource(): ...\n\n@pytest.fixture\ndef dependent(resource):\n    print(resource)\n\ndef test_use(resource):\n    print(resource)\n```\n\nFinding references on `def resource` (or on a parameter that requests)\nit reports the function definition, both requesting parameters, and\ntheir uses:\n\n```text\ninfo[references]: Found 5 references\n  --\u003e src/test_example.py:5:5\n   |\n 5 | def resource(): ...\n   |     --------\n 6 |\n 7 | @pytest.fixture\n 8 | def dependent(resource):\n   |               --------\n 9 |     print(resource)\n   |           --------\n10 |\n11 | def test_use(resource):\n   |              --------\n12 |     print(resource)\n   |           --------\n```\n\nFixtures with an explicit name (i.e.,\n`@pytest.fixture(name\u003d\"public_name\")`) are treated specially: we use\n`\"public_name\"` for the fixture reference target and preserve ordinary\nreference behaviour for the actual function name.\n\nCloses https://github.com/astral-sh/ty/issues/4115.\n\n## Test Plan\n\nSee included tests.\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "92bb19801594c744da6c008c1235f49cc7b3b161",
      "tree": "438aa1d3077029008af6592e412eb043d63c3370",
      "parents": [
        "f5e6c6ae8fb37bc88e2de140208bb1811d551498"
      ],
      "author": {
        "name": "Rob Hand",
        "email": "146272+sinon@users.noreply.github.com",
        "time": "Sat Aug 29 01:20:42 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 17:20:42 2026 -0700"
      },
      "message": "[ty] Align conformance.py counts with upstream (#28132)\n\n## Summary\nThe upstream https://github.com/python/typing generated html now has a\nbreakdown of total test cases passing for each typechecker.\n\nThese total don\u0027t match what the `conformance.py` emits in it\u0027s summary:\nhttps://github.com/astral-sh/ruff/pull/28001#issuecomment-5410032559\n\n\u003e The number of fully passing files held steady at 112/136. \n\nversus \n\n\u003cimg width\u003d\"235\" height\u003d\"132\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/6af76b8d-9002-4b00-ac87-b55afb0c15f8\"\n/\u003e\n\nThis is because the summary is generated from `TestCase` which track\n`ty` diagnostics, if there are no diagnostics because the relevant test\nis designed to emit none they are not included in the count.\n\nSo instead we also use the full `test_files` to fill in the missing\nstats.\n\nNOTE: I have also bumped `CONFORMANCE_SUITE_COMMIT` which brings in some\nrecent spec related changes (not just version bumps)\n \n\n## Test Plan\n\n`CONFORMANCE_SUITE_COMMIT\u003dcf943ccbea5596ef969eec6e2260097ff697b7ba uv\nrun --no-project --python 3.12 scripts/conformance.py --old-ty uvx\nty@0.0.75 --new-ty uvx ty@0.0.75 --tests-path ~/dev/typing/conformance`\n\n```md\n## [Typing conformance results](https://github.com/python/typing/blob/cf943ccbea5596ef969eec6e2260097ff697b7ba/conformance/)\n\n### No changes detected ✅\n\n\u003cdetails\u003e\n\u003csummary\u003eCurrent numbers\u003c/summary\u003e\n\n\u003cbr\u003e\nThe percentage of diagnostics emitted that were expected errors held steady at \u003cb\u003e97.79%\u003c/b\u003e. The percentage of expected errors that received a diagnostic held steady at \u003cb\u003e94.41%\u003c/b\u003e. The number of fully passing files held steady at \u003cb\u003e121/145\u003c/b\u003e.\n\n\u003c/details\u003e\n```"
    },
    {
      "commit": "f5e6c6ae8fb37bc88e2de140208bb1811d551498",
      "tree": "f7f9533b6b5973603e0b979bd65d03823d878995",
      "parents": [
        "b9b193d0c176522d4a177e6b46c931cf6f40cfff"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 17:13:41 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 17:13:41 2026 -0700"
      },
      "message": "[ty] Avoid repeated traversal of shared alias arguments (#28147)\n\nSearching generic alias arguments for `Self` or a type variable starts a\nfresh type walk for each argument. Shared nested specializations are\ntherefore visited repeatedly, which can make method binding take\nexponential time even when its annotations contain no `Self`.\n\nUse a shared visitor implementation for both searches, with one\nrecursion guard per search. It visits explicit alias arguments,\nincluding arguments unused by the alias body, without evaluating alias\nbodies or other lazy attributes. Different specializations of the same\nalias remain distinct.\n\nFollow-up to #28117.\n\n## Test plan\n\nAdd mdtests for `Self` used only in an aliased parameter, including\naccepted and rejected arguments, and nested uses of the same alias in an\ninherited method. Simplify the protocol fixtures while retaining\nseparate method and property cases for structural `Self` binding.\n\nA minimized example with 32 levels of shared alias arguments completes\nin about 40 ms; the prior implementation hit a 12-second timeout in the\nsame debug-build setup."
    },
    {
      "commit": "b9b193d0c176522d4a177e6b46c931cf6f40cfff",
      "tree": "45b2dd836d54c52027e28675264f03bc894be1d5",
      "parents": [
        "44713bd0f9b5b8364b5f30dd0333a5af6d3140c3"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 15:31:03 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 22:31:03 2026 +0000"
      },
      "message": "[ty] Guard recursive meta-type expansion (#28143)\n\nComputing `type(value)` for recursive type aliases can overflow the\nstack. This change guards\nmeta-type projection so recursive aliases terminate while preserving the\npossible classes through\nfinite specialization cycles. When type arguments grow without bound,\ninference conservatively\nreturns `type`.\n\nThe guard tracks active alias specializations and recursive identities\nwithout reusing partial\nresults from another branch of a mutually recursive alias.\n\n## Test plan\n\nAdded mdtests cover:\n\n- Mutually recursive aliases combined in an intersection, retaining all\npossible classes.\n- Rotating generic type arguments through a finite cycle, retaining\n`type[int | str]`.\n- Generic type arguments that grow without bound, terminating with the\nconservative result `type`."
    },
    {
      "commit": "44713bd0f9b5b8364b5f30dd0333a5af6d3140c3",
      "tree": "2cecdac37117199e1556a7f0172e6cc33e6a7cfc",
      "parents": [
        "da394061b2a98045814e11790f1de06efb13ecee"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 15:26:38 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 22:26:38 2026 +0000"
      },
      "message": "[ty] Invalidate member narrowing across loop iterations (#28009)\n\nReassigning an object inside a loop leaves narrowing of its attributes\nor indexed elements in effect from before the loop. This can make a\n`while` condition appear permanently true and cause ty to treat\nreachable code after the loop as unreachable.\n\nCreate loop-header definitions for places invalidated by reassignment,\nand include their fallback member types when computing narrowing across\nloop-back paths. Preserve narrowing established after reassignment and\nthe initial narrowing when a reassignment cannot reach another\niteration. Propagate invalidation through nested loop headers with\nmemoization.\n\nExclude synthetic loop headers from attribute-assignment discovery:\nrebinding a receiver does not write its attributes or initialize `Final`\nvalues. Treat discarded dictionary-key assignments as invalidations so\nrejected replacements and property-setter inputs do not become inferred\nkey values on subsequent iterations.\n\nFixes https://github.com/astral-sh/ty/issues/4379.\n\n## Ecosystem\n\nThe ecosystem run removes 55 false-positive Pydantic errors and one\nPyodide warning. It adds three expected websockets diagnostics by making\na previously unreachable `join` reachable; the existing annotations\nallow mixed text and binary fragments. Minimized reproductions confirm\nthese changes are expected, with no regressions found.\n\n## Test plan\n\n- Extend `while` and `for` mdtests to cover condition, body, and\nnormal-exit narrowing after object replacement, explicit `continue`,\nnested-loop early exits, nested attributes, and subscripts.\n- Verify that break-only paths preserve initial narrowing and that\nassertions on replacement objects narrow the next iteration.\n- Verify method binding before receiver-rebinding loops and at external\ncalls, and distinguish actual `Final` initialization inside loops from\nreceiver rebinding.\n- Cover accepted and rejected dictionary replacements, narrowing after\nrejected replacements, and property setters whose input types differ\nfrom their getters, including loop-carried dictionary values."
    },
    {
      "commit": "da394061b2a98045814e11790f1de06efb13ecee",
      "tree": "d1501dec2ed025e0bac4a20780726bc9d5915afb",
      "parents": [
        "e837e8bf7738cb97dce61b7efe874c831a5ccd9a"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Fri Aug 28 15:13:14 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 15:13:14 2026 -0700"
      },
      "message": "[ty] Model pytest fixture exposures and name sources (#28074)\n\n\u003c!--\nThank you for contributing to Ruff/ty! To help us out with reviewing,\nplease consider the following:\n\n- Does this pull request include a summary of the change? (See below.)\n- Does this pull request include a descriptive title? (Please prefix\nwith `[ty]` for ty pull\n  requests.)\n- Does this pull request include references to any relevant issues?\n- Does this PR follow our AI policy\n(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?\n--\u003e\n\n## Summary\n\nIn anticipation of [\"find references\" for pytest\nfixtures](https://github.com/astral-sh/ruff/pull/28075), this PR extends\nthe semantic fixture model to preserve the information about how\nfixtures are exposed through Python bindings and requested by\nparameters.\n\nMore specifically:\n\n- We extend `FixtureExposure`[^1] with a few fields to help us\nunderstand how bindings might contribute to the name by which a fixture\nis requested.\n- We introduce a new API, a Salsa query named\n`fixture_exposures_for_definition`, which returns the available\nexposures contributed by a function, import, or stub definition.\n- We extend `FixtureBinding` with a new `exposures` field, which records\nthe equally viable exposures through which a parameter request resolves\nto its fixture\n\n[^1]: Remember that a `FixtureExposure` is effectively the name by which\na fixture should be requested, and the definition of the fixture\nfunction itself.\n\nThe new interfaces remain private here and will be exposed to `ty_ide`\nby the follow-up PR.\n\n## Test Plan\n\nSee included tests.\n\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "e837e8bf7738cb97dce61b7efe874c831a5ccd9a",
      "tree": "8ef9349e65a2b6203718d0f9bbc15e934b99fb64",
      "parents": [
        "ee3e08772a51f4fd8a8ffe0789ecd0aac984842e"
      ],
      "author": {
        "name": "Pikammmmm",
        "email": "124257101+pikammmmm@users.noreply.github.com",
        "time": "Fri Aug 28 23:03:30 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 21:03:30 2026 +0000"
      },
      "message": "[`flake8-bugbear`] Fix panic on `match` subjects (`B031`) (#27781)\n\nFixes #26624\n\n---------\n\nCo-authored-by: Brent Westbrook \u003cbrentrwestbrook@gmail.com\u003e"
    },
    {
      "commit": "ee3e08772a51f4fd8a8ffe0789ecd0aac984842e",
      "tree": "970d3bcf9f330b11d1b54d03806ddfb8aa8b7f72",
      "parents": [
        "877599b4e86fdfdc8a093042b6d30c70c04fc69f"
      ],
      "author": {
        "name": "water",
        "email": "672684719@qq.com",
        "time": "Sat Aug 29 04:29:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 16:29:59 2026 -0400"
      },
      "message": "[`ruff`] Remove `lint.external` hint for Ruff-specific suppressions (`RUF102`) (#27923)\n\n## Summary\n\nRemove the \"Add non-Ruff rule codes to the `lint.external` configuration\noption\" help message from the suppression path (RUF102/RUF104).\n\nThe `# ruff: ignore` directive is a Ruff-specific suppression mechanism.\nWhen a user writes an unknown code in a `# ruff: ignore` directive, it\nis typically a typo or a rule name with preview disabled — not a\nnon-Ruff rule code. The help message suggesting to add the code to\n`lint.external` is therefore misleading in this context.\n\nThe noqa path (RUF102) still handles this tip for `# noqa` directives,\nwhich is the appropriate context for external rule codes.\n\nCloses #27880.\n\n## Changes\n\n1. **suppression.rs**: Removed the `has_unknown_code` field from\n`SuppressionDiagnostic` and the help message that was emitted when it\nwas set.\n2. **ignore.md**: Removed the \"Add non-Ruff rule codes\" help lines from\nsuppression test snapshots.\n3. **range_suppressions.snap**: Updated snapshot to reflect the removal\nof the help message."
    },
    {
      "commit": "877599b4e86fdfdc8a093042b6d30c70c04fc69f",
      "tree": "7751ad23f9833fd677896b0e20b5ee4b8341da0d",
      "parents": [
        "78e34fdb3f4098951dc6b5d3b12010dd96f99b1e"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 13:09:45 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:09:45 2026 -0700"
      },
      "message": "[ty] Mark Self override narrowing as a known limitation (#28144)\n\nThe Self override tests currently describe accepting narrowed parameters\nas valid. A call through a base-class reference can pass a base-class\ninstance that the subclass override does not accept, so these cases\nshould document missing diagnostics instead.\n\nAdd TODO comments for the seven missing diagnostics and revise the prose\nto describe the acceptance as a limitation. Checker behavior and\ndiagnostic expectations are unchanged.\n\nRelated: [ty#2255](https://github.com/astral-sh/ty/issues/2255) and\n[ty#4133](https://github.com/astral-sh/ty/issues/4133).\n\n## Test plan\n\nThe existing Liskov mdtests cover implicit and explicit instance\nreceivers, generic class specialization, and classmethods. These\nscenarios keep their current expectations while recording the missing\noverride errors. The tests for incompatible Self return types remain\nunchanged."
    },
    {
      "commit": "78e34fdb3f4098951dc6b5d3b12010dd96f99b1e",
      "tree": "820a630eaef2db50dbd0a648ea0033a09bc86fec",
      "parents": [
        "edd48cc5f6b5e17d28a0f48ae78265b877b4080b"
      ],
      "author": {
        "name": "whn",
        "email": "142425816+Whning0513@users.noreply.github.com",
        "time": "Sat Aug 29 03:54:17 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 19:54:17 2026 +0000"
      },
      "message": "[`pyupgrade`] Clarify default `encoding` argument handling (`UP012`) (#27315)\n\nHi! This updates UP012 documentation to describe both supported cases:\nreplacing ASCII literals with bytes literals and removing redundant\nexplicit UTF-8 encoding arguments. It also handles the latter for\ngeneral expressions such as `str(exc).encode(\"utf-8\")`, which the rule\npreviously skipped.\n\nFixes #26854"
    },
    {
      "commit": "edd48cc5f6b5e17d28a0f48ae78265b877b4080b",
      "tree": "98df0709b190a803a8f02b2e0fe1ebdd3475cfee",
      "parents": [
        "d580d7ee8bf1a55f045e6e391de98079f946ebca"
      ],
      "author": {
        "name": "Ben Tattersley",
        "email": "jamtat@users.noreply.github.com",
        "time": "Fri Aug 28 20:44:17 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:44:17 2026 -0700"
      },
      "message": "[ty] Bind Self through generic type aliases (#28117)\n\n## Summary\n\nty incorrectly rejects structural protocol implementations when `Self`\nappears as an argument to a generic type alias:\n\n```python\ntype Identity[T] \u003d T\n\nclass Cloneable(Protocol):\n    def clone(self) -\u003e Identity[Self]: ...\n\nclass Concrete:\n    def clone(self) -\u003e Identity[Self]:\n        return self\n```\n\n`Self` binding uses a preliminary check to avoid applying a type mapping\nwhen a type does not contain `Self`. Because type alias bodies are lazy,\nthis check did not inspect the alias specialization and therefore missed\nthe `Self` argument in `Identity[Self]`.\n\nInspect generic type alias arguments when checking for `Self`, without\nevaluating the alias body. The existing type mapping can then bind\n`Self` normally in method parameters and returns, property types, and\nattribute annotations.\n\nFixes astral-sh/ty#4409.\n\n## Test Plan\n\n- Added mdtests for binding aliased `Self` in bound method parameters,\nreturn types, and attribute annotations.\n- Added protocol mdtests covering aliased and direct structural method\nimplementations and property return types.\n- Ran the full `ty_python_semantic` test suite.\n- Ran Clippy with all targets and features and warnings denied.\n- Ran the applicable repository hooks."
    },
    {
      "commit": "d580d7ee8bf1a55f045e6e391de98079f946ebca",
      "tree": "42d5b8a2270754bfe7963cdba8260a256be76bef",
      "parents": [
        "4f0b91879316f5a7f4e562a34a8760eca1073080"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Fri Aug 28 14:10:51 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 14:10:51 2026 -0400"
      },
      "message": "Combine redundant `match` arms in `pathlib` rules (#28139)\n\nSummary\n--\n\nNoticed this while reviewing #28027.\n\nTest Plan\n--\n\nExisting tests"
    },
    {
      "commit": "4f0b91879316f5a7f4e562a34a8760eca1073080",
      "tree": "6bfae8e87b3c784e3ff1f4aa93899c3a19350f94",
      "parents": [
        "c2b2eda972b16f000249e30f3bc590ded81d3d82"
      ],
      "author": {
        "name": "fly1d",
        "email": "zhanghui121383@gmail.com",
        "time": "Sat Aug 29 01:58:48 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:58:48 2026 -0400"
      },
      "message": "[`flake8-use-pathlib`] Add display-only fix for `os.listdir` (`PTH208`) (#28027)\n\n## Summary\n\nAdds a display-only suggestion for `PTH208` diagnostics with a path\nargument:\n\n```python\nPath(path).iterdir()\n```\n\nThe suggestion is intentionally not an automatic fix because\n`os.listdir` returns strings or bytes, while `Path.iterdir()` returns\n`Path` objects and can change downstream behavior. The existing\ndiagnostic remains unchanged, and file-descriptor calls do not receive\nthe suggestion.\n\nPart of #2331.\n\n## Verification\n\n- `INSTA_FORCE_PASS\u003d1 INSTA_UPDATE\u003dalways cargo test -p ruff_linter\nflake8_use_pathlib::tests --lib` (34 passed)\n- `cargo fmt --all -- --check`\n- `git diff --check`\n\n\u003e OpenAI Codex assisted with implementation and verification. The\ncontributor reviewed the change before submission.\n\n---------\n\nCo-authored-by: fly1d \u003c309400591+fly1d@users.noreply.github.com\u003e\nCo-authored-by: Brent Westbrook \u003cbrentrwestbrook@gmail.com\u003e"
    },
    {
      "commit": "c2b2eda972b16f000249e30f3bc590ded81d3d82",
      "tree": "7959753335c0ab9fccdc35a22b1281a756d76fa3",
      "parents": [
        "ef08e25617f1a5da62024707ae0dbb8c090ea61d"
      ],
      "author": {
        "name": "Tom Kuson",
        "email": "mail@tjkuson.me",
        "time": "Fri Aug 28 18:22:30 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:22:30 2026 -0400"
      },
      "message": "[`flake8-async`] Document thread offloading (`ASYNC240`) (#28008)\n\n## Summary\n\nDocument `asyncio.to_thread` and `loop.run_in_executor` as alternatives\nfor ASYNC240 and make the diagnostic library-neutral.\n\n\nI noticed that ASYNC240\u0027s message and documentation recommended anyio\nand trio as alternatives to blocking path operations. This is confusing\nin projects where anyio or trio are not used. When making the change I\nalso noticed some other issues (the rule reports `os.path` _functions_\nrather than methods and the docs referenced a nonexistent\n`pathlib.Path.dirname` method).\n\nAdds a reference to aiofiles which is a very popular library for async\npath operations.\n\n## Test Plan\n\nBuilt the docs and looked at it."
    },
    {
      "commit": "ef08e25617f1a5da62024707ae0dbb8c090ea61d",
      "tree": "2b64fe5a39b6ae6b63d2243b1e3e664f974a5fb0",
      "parents": [
        "4e4e9f5e73dab6e1e34dd186294e02303d2b07a2"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Fri Aug 28 18:18:07 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 18:18:07 2026 +0100"
      },
      "message": "[ty] Format lint documentation the same way we format mdtests (#28135)"
    },
    {
      "commit": "4e4e9f5e73dab6e1e34dd186294e02303d2b07a2",
      "tree": "0d9bca1c0cc582b3765ba4067bb8ce78bcaacb86",
      "parents": [
        "088b7b5f77fcb05994fd2a17cbc940cd9edb341a"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 10:07:24 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 10:07:24 2026 -0700"
      },
      "message": "[ty] Rename Truthiness::and_else to and_then (#28137)\n\nThe lazy conjunction helper\u0027s name, `Truthiness::and_else`, does not\nfollow Rust\u0027s `Option::and_then` convention. Rename it to `and_then` and\nupdate its chained-comparison caller and existing tests, without\nchanging evaluation behavior.\n\nAddresses [the review feedback on\n#28082](https://github.com/astral-sh/ruff/pull/28082#discussion_r3878774178).\n\n## Test plan\n\nUpdated the existing truthiness test to use the new name. It covers all\nnine input combinations and verifies that the closure runs exactly once\nwhen the left side may be true, and never when it is always false."
    },
    {
      "commit": "088b7b5f77fcb05994fd2a17cbc940cd9edb341a",
      "tree": "bc9e2b5888c92b5db4260af31d4bb03465bfae08",
      "parents": [
        "3c77dcc2a5c04fc0c54ce95cd51068734015f3e0"
      ],
      "author": {
        "name": "chiri",
        "email": "chirizxc@proton.me",
        "time": "Fri Aug 28 19:39:33 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:39:33 2026 -0400"
      },
      "message": "Add another example and glob reference for `lint.per-file-ignores` (#28106)\n\n## Summary\n\nI came across this syntax while exploring the `glob` crate and found it\nquite useful. I think it would be worth adding a short example to the\ndocumentation.\n\n## Test Plan"
    },
    {
      "commit": "3c77dcc2a5c04fc0c54ce95cd51068734015f3e0",
      "tree": "f2fe5c3732836c20a084f17402208a010e0fe3bf",
      "parents": [
        "1f7015ee7948ea457d3bffe5353651ca9cf92484"
      ],
      "author": {
        "name": "Zsolt Dollenstein",
        "email": "zsol.zsol@gmail.com",
        "time": "Fri Aug 28 17:36:09 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 16:36:09 2026 +0000"
      },
      "message": "[ty] Warn when uv metadata cannot support dependency checks (#28133)\n\n`missing-direct-dependency` currently skips checks without a diagnostic\nwhen dependency metadata conversion or Python environment checks fail.\nThis PR reports one `uv-metadata` warning for the project, including the\nreason and recovery advice where available. Ordinary type checking\ncontinues.\n\nThe warning is emitted only when the rule is enabled globally or in a\nfile override. Existing uv discovery and refresh errors take precedence.\n\nDependency-free virtual workspaces no longer require module ownership\nmetadata, avoiding a spurious warning from the new diagnostics\nimplemented here.\n\n\u003e [!NOTE]\n\u003e A uv\n[workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/)\ngroups projects under a shared lockfile. The projects do not all need to\nbe installed as Python packages: uv also supports [“virtual”\nprojects](https://docs.astral.sh/uv/concepts/projects/dependencies/#virtual-dependencies),\nfor which it installs dependencies without building or installing the\nproject itself.\n\u003e If a workspace contains only such non-package projects and has no\nexternal dependencies, there are no installed modules to associate with\npackages. Its empty module ownership map is valid. ty previously treated\nthat metadata as unavailable and disabled dependency checks without a\ndiagnostic. This PR accepts it instead, avoiding a spurious uv-metadata\nwarning from the new diagnostics.\n\nDepends on #28001."
    },
    {
      "commit": "1f7015ee7948ea457d3bffe5353651ca9cf92484",
      "tree": "8b2e5bfdfdb2db067601820f129bf1f89c2c95b2",
      "parents": [
        "947179d7c6312e1ad3255f590fe39e3b7ebd4411"
      ],
      "author": {
        "name": "Jelle Zijlstra",
        "email": "jelle@openai.com",
        "time": "Fri Aug 28 09:29:38 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:29:38 2026 -0400"
      },
      "message": "[`flake8-async`, `pylint`] Recognize `builtins.open` (`ASYNC230`, `PLW1514`) (#28021)\n\nRecognize explicitly qualified and imported builtin open calls when\nchecking for blocking I/O and missing text encodings.\n\nFixes #28012."
    },
    {
      "commit": "947179d7c6312e1ad3255f590fe39e3b7ebd4411",
      "tree": "f760ca9e61b32f235f9ce9efa1d9a546ec04cbe9",
      "parents": [
        "c0b78da5b2a6923ea12eb57888b042d72ad53369"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Fri Aug 28 12:24:36 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:24:36 2026 -0400"
      },
      "message": "Enable `lint.preview` and missing default rules in our Ruff config (#28112)\n\nSummary\n--\n\nThis PR updates our ruff-pre-commit dependency to 0.16.5, which supports\nthe new category selectors,\nand then enables `lint.preview` and the new default categories so that\nwe can dogfood them on our\ncode. There were 66 diagnostics initially, which I also fixed or\nignored:\n\n```\n28\tFURB113 \t[ ] repeated-append\n12\tASYNC240\t[ ] blocking-path-method-in-async-function\n 6\tRUF105  \t[*] noqa-comments\n 5\tPLW1510 \t[ ] subprocess-run-without-check\n 3\tPLW0108 \t[ ] unnecessary-lambda\n 2\tEXE001  \t[ ] shebang-not-executable\n 2\tRUF039  \t[*] unraw-re-pattern\n 1\tB024    \t[ ] abstract-base-class-without-abstract-method\n 1\tFURB142 \t[*] for-loop-set-mutations\n 1\tPERF102 \t[ ] incorrect-dict-iterator\n 1\tPLC0207 \t[*] missing-maxsplit-arg\n 1\tPLW0127 \t[ ] self-assigning-variable\n 1\tTRY004  \t[ ] type-check-without-type-error\n 1\tPLW0120 \t[*] useless-else-on-loop\n 1\tTRY201  \t[ ] verbose-raise\n ```\n\nOf these, I ignored `repeated-append` and `blocking-path-method-in-async-function`. I think they are\nstill reasonable defaults, just less useful/important in our uses.\n\n0.16.5 also includes https://github.com/astral-sh/ruff-pre-commit/pull/159, so I removed `markdown` from our main `ruff-format` hook, leaving it for the later mdtest formatting stage.\n\nI also had to make a few changes related to config discovery in tests:\n- move our cached ecosystem checkouts to `$RUNNER_TEMP` instead of inside of our checkout\n- convert some CLI tests to use the `CliTest` helpers\n- add `configurationPreference: editorOnly` to some notebook tests\n\nIn these cases, our own Ruff settings were being inherited by test code. This likely affects many other tests, but I only updated the obviously failing ones for now. I\u0027m least confident about the notebook tests, but as far as I can tell from the history (https://github.com/astral-sh/ruff/pull/11944, https://github.com/astral-sh/ruff/pull/22810), the specific diagnostics are not the important parts being asserted.\n\nTest Plan\n--\n\nCI on this PR"
    },
    {
      "commit": "c0b78da5b2a6923ea12eb57888b042d72ad53369",
      "tree": "587389aa43f91c029a906233001938c34c322490",
      "parents": [
        "4125782a78330556641cf0286e2ff6fa65094060"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 07:25:43 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 07:25:43 2026 -0700"
      },
      "message": "[ty] Preserve type variables in bounded generic defaults (#28122)\n\nA type parameter default such as `Box[T]` can incorrectly reject `T`\nwhen `Box` has a bounded or constrained type parameter, leaving the\ndefault specialized with `Unknown`. This change accepts compatible type\nvariables and preserves them so applying the default substitutes the\nactual type argument.\n\nBind unresolved type variables in a temporary copy when validating\ngeneric arguments against bounds or constraints. Keep the original type\nin the specialization so defaults can still be bound to the generic that\nuses them.\n\nFixes https://github.com/astral-sh/ty/issues/4408.\n\n## Test plan\n\nAdded mdtests for defaults containing bounded and constrained type\nvariables in both PEP 695 and legacy generic syntax. The tests cover\nclass and type alias defaults, substitution through nested tuple\narguments, incompatible bounds and constraints, and rejection of\ninvariant list arguments."
    },
    {
      "commit": "4125782a78330556641cf0286e2ff6fa65094060",
      "tree": "bc6d17d943429145c47dc1c3e0d1665bf17849c3",
      "parents": [
        "f4839591d61e986635b69a16bda0847e4d27ad72"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 07:24:35 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 07:24:35 2026 -0700"
      },
      "message": "[ty] Optimization: reject impossible eager protocol comparisons early (#28081)\n\nType checking could spend substantial time expanding recursive protocol\nmembers even when a finite member already made an eager comparison\nimpossible. This is particularly costly when checking explicit receiver\nannotations on invariant protocols.\n\nExtend the finite-member shortcut to eager comparisons between\nspecializations of the same protocol. Incompatible finite requirements\nreject the structural relation immediately; matching finite requirements\nstill fall back to the full interface, since recursive members may\nimpose additional requirements. Preserve the nominal shortcut for union\nsimplification and the existing lazy structural comparisons.\n\nPreserve nested protocol mismatch explanations when overload checking\nhas already compared the same types. During diagnostic generation,\nrecompute cached unsatisfiable comparisons while reusing satisfiable\nconstraints and keeping the existing recursion guards.\n\nOn the `Chain.flatten` reproducer, the median time across five warmed\ndebug runs with a single worker and concise output falls from 1.674 s to\n0.039 s (about 43× faster). Full and concise diagnostics are unchanged\nfor this reproducer.\n\nPart of https://github.com/astral-sh/ty/issues/4269.\n\n## Test plan\n\n- Cover an invariant recursive `Chain` protocol with an incompatible\n`flatten` receiver and a valid receiver that preserves its element type.\n- Verify that matching finite members cannot hide incompatible recursive\nrequirements.\n- Preserve structural assignability when recursion occurs in the source\nspecialization, including lazy bound-method comparisons during union\nsimplification.\n- Preserve nested protocol-member explanations for a recursive callable\nprotocol assigned to a union, including the incompatible `payload` type.\n- Keep repeated successful alias comparisons fast when diagnosing later\ntuple or callable parameter mismatches, including explicit receivers\nthat leave type-variable constraints.\n- Check cache reuse and shared recursion guards when a completed\ncomparison is repeated."
    },
    {
      "commit": "f4839591d61e986635b69a16bda0847e4d27ad72",
      "tree": "13dfa65128c34b76b0e08ca81a0f526fa82d88de",
      "parents": [
        "0b7da8ce6e2c2e760cdabb7f12b456032d2841c9"
      ],
      "author": {
        "name": "Zsolt Dollenstein",
        "email": "zsol.zsol@gmail.com",
        "time": "Fri Aug 28 15:01:44 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 14:01:44 2026 +0000"
      },
      "message": "[ty] Add a missing-direct-dependency rule (#28001)\n\nThis PR replaces #25027. It adds an opt-in check for imports of\ninstalled distributions that the current project does not declare\ndirectly in `pyproject.toml`.\n\n## Overview\n\nThe check uses ty\u0027s existing uv workspace integration (`TY_UV\u003d1`) to\ncall `uv workspace metadata --frozen --active`. uv now provides module\nownership without synchronizing the environment, so ty can read the\nmetadata without installing packages or updating `uv.lock`.\n\nThe metadata provides:\n\n1. Each workspace project\u0027s runtime and optional dependencies.\n2. Dependencies declared in dependency groups.\n3. Importable modules and their owning distributions, including source\npaths for editable installs.\n\nDuring import inference, ty maps each resolved import to its\ndistribution and compares it against the importing project\u0027s direct\ndeclarations. If the declaration is missing, ty emits\n`missing-direct-dependency`, naming the distribution to add. Each\ndistribution is reported once per file, at its first unsuppressed\nimport.\n\nThe rule is disabled by default and requires an existing, synchronized\nenvironment. Unavailable or incompatible dependency metadata disables\nthe check; rejected dependency metadata produces a debug message.\n\n## Nuances\n\n- Editable installs may lack entries in uv\u0027s module-owner map. ty uses\ntheir source paths to attribute resolved imports.\n- Files identified as package code require runtime or optional\ndependencies. Non-package files, such as tests and scripts, can also use\ndependency groups, but those groups\u0027 transitive dependencies still need\ndirect declarations.\n- For `from package import child`, a resolved child module is checked\nagainst its own distribution rather than the parent\u0027s. Imports with\nunknown or ambiguous ownership are skipped.\n- Ownership metadata is used only when the explicitly selected Python\nenvironment matches uv\u0027s environment.\n- The current integration covers directory checks. Explicit file\narguments, `--config-file`, and standalone PEP 723 scripts do not use\nthe workspace\u0027s dependency declarations.\n\nThe schema and rule documentation are regenerated. Most of the\nrule-documentation diff consists of updated source-line links."
    },
    {
      "commit": "0b7da8ce6e2c2e760cdabb7f12b456032d2841c9",
      "tree": "f75e0a92ff4883e011701bdeb5929db74df77717",
      "parents": [
        "298858ebb6ea34bdc5a94172c6b805838fab6602"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri Aug 28 06:29:34 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 06:29:34 2026 -0700"
      },
      "message": "[ty] Use the subclass receiver when checking method overrides (#28124)"
    },
    {
      "commit": "298858ebb6ea34bdc5a94172c6b805838fab6602",
      "tree": "1762c76b9be33a27f01f490f97a70dcb7f039186",
      "parents": [
        "d165c3ca1236868f2f289244e107c04812fc1e58"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Fri Aug 28 15:06:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 15:06:51 2026 +0200"
      },
      "message": "[ty] Update ecosystem-analyzer (#28131)\n\n## Summary\n\nBump ecosystem-analyzer from `fcce2284` to `a31ef389`, including:\n\n- HTML escaping and restricted report links\n([#181](https://github.com/astral-sh/ecosystem-analyzer/pull/181)).\n- Git subprocess calls in place of GitPython\n([#179](https://github.com/astral-sh/ecosystem-analyzer/pull/179)).\n- Suppression of removed `unknown-rule` diagnostics in Markdown comments\n([#182](https://github.com/astral-sh/ecosystem-analyzer/pull/182))."
    },
    {
      "commit": "d165c3ca1236868f2f289244e107c04812fc1e58",
      "tree": "37d501b50147c6ce4bfabca47ddeb42d5cb94147",
      "parents": [
        "47c81c8b4aa5e9773a0baa7e77c12f35b09737b2"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 13:09:04 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:09:04 2026 +0200"
      },
      "message": "[ty] Cancel superseded queued uv requests (#28108)\n\n## Summary\n\nNow that we queue all scripts on startup, I thought it\u0027s important to\nhave a way to cancel already queued sync requests if they\u0027re superseded\nby a new sync.\n\nThe implementation here is relatively simple. We use our existing\n`CancellationToken` and check it before a worker invokes `uv metadata`.\nThis is similar to what we do in the LSP.\n\nThe downside of this is that the new request for the cancelled script\nwill, again, be queued at the back. I think this is fine for now. We can\nexplore with more elaborate schemas later.\n\n## Test Plan\n\nThe focused CLI, LSP, and file-watching tests pass."
    },
    {
      "commit": "47c81c8b4aa5e9773a0baa7e77c12f35b09737b2",
      "tree": "c8e6c04928c40af9657174763e2dd219cc373e6e",
      "parents": [
        "0e60c897b80a1ed286a7d463ad31a324c8f33813"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 13:09:04 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:09:04 2026 +0200"
      },
      "message": "[ty] Move script environment initialization to the CLI and LSP (#28038)\n\n## Summary\n\nThis PR moves from a lazy PEP 723 script discovery do eagerly discover\nall scripts as part of the project\u0027s file discovery.\n\n\nThere are two motivations for doing this:\n\n1. Initializing the script\u0027s environment before checking the script is\nnot sufficient. Other files can import the script, and type inference\ncan then resolve `file_settings` and `python_version_with_source` which\nare both environment dependent (the former less so, but uv can tell us\nthe python version). Initializing all scripts before we start checking\nensures that type inference sees the right environment even when a\nscript has been imported. This is the bug that Zsolt identified in my\ninitial PEP 723 PR.\n2. Today, the LSP only initialized scripts when the user opened them.\nHowever, that means that their environment isn\u0027t available as part of\nfind references and workspace symbols. This can result in missed\nreferences (note, making find references return all project and script\nreferences when searching for references of a shared dependency requires\na few changes to find references as well, but this sets the foundation\nfor it). Initializing all environments when opening the project ensures\nthe environments are available when running find references (or the user\ncan rerun find references once all syncs are complete, but they don\u0027t\nhave to open every script just to make sure that find references will\nshow the references in them)\n3. Knowing all scripts simplifies file watching because we now have\ndistinct points where script environments can change, and may require\nupdating the watched paths.\n\n\nNow, there are a few downsides as well, but I think this is the right\ntrade off (honestly, I don\u0027t know how to fix 1 easily without it, and 3\nwas the main reason why I considered this approach even before Zsolt\nfound the bug).\n\n* Unlike before, the LSP now indexes and reads all project files. This\ncan mean that startup now takes a second or so on very large projects\nwhere it was instant before, because we didn\u0027t do any form of indexing.\nThis also means that the LSP retains more memory for projects that don\u0027t\nuse workspace diagnostics (or find references), because the LSP now\nloads all files into memory to determine whether a file is a script.\nThis is inherent to the approach. I suspect that we can mitigate the\n\"all source in memory\" in the future by only storing compressed source\ntext, which should reduce memory usage by about 70%.\n* We now sync all scripts on LSP startup. I could see this become a\nproblem in workspaces with many (with 100 or 1000s scripts). If this\nbecomes a problem, I think we can a) limit script sync to on-open,\naccepting that find-references etc. may require opening the relevant\nscripts and/or b) allow users to selectively sync scripts by using a LSP\ncommand. For now, I think syncing all scripts should be fine, given that\nuv is fast (the first time might take a while)\n* I had to make the uv sync queue unbounded. It\u0027s still bounded by the\nnumber of scripts and projects. But we no longer apply back pressure if\nthe sync requests start piling up. The reason I had to remove it is\nbecause we don\u0027t want a `didChange` or `didOpen` to block, only because\nthere are already 8 queued scripts. But more importantly, we don\u0027t want\nto block the LSP startup until all scripts have synchronized, but\nqueuing them up during startup would just have done that (because the\nmain loop blocks everytime time the queue is full). I think this is\nokay, and I\u0027ll add another PR on top of this one that adds cancellation\nsupport for sync requests. I also think that our previous back pressure\nwasn\u0027t as impactful as I thought\n* We now sync scripts before checking the files. That means, ty no\nlonger makes progress on checking other project files while the syncs\nare pending. This is the one I\u0027m the most bumped about. It was so cool\nthat ty often checked the entire project while waiting on uv to finish\nsyncing. To mitigate, I increased the worker pool to N/4 where N is max\nparallelism and, while testing, I also found that syncinc all scripts,\nat least in Ruff\u0027s repo, is instant once uv is warm.\n\nI think this also gets us into a position where adding support for\nnested projects becomes much easier, because they\u0027ll naturally fit in.\nWe\u0027ll discover nested projects as part of indexing `Files` (we\u0027d need to\nmove that off `Project`), synchronizing their environments can happen at\nthe exactly same points as synchronizing scripts. The biggest remaining\nlift is to change `ty_project` and `ProjectDatabase` to no longer assume\nthat there\u0027s a single `Project` (but there is still one main project).\n\n\n## Test Plan\n\nCLI\n\n\nhttps://github.com/user-attachments/assets/db370cde-d9db-4c85-8772-06b608ce6587\n\n\n\nhttps://github.com/user-attachments/assets/ae6e825a-6814-4dc0-8b2f-630469de1a8d"
    },
    {
      "commit": "0e60c897b80a1ed286a7d463ad31a324c8f33813",
      "tree": "ef8e8183a69067e7c7f2e16107ee4c4a5853e320",
      "parents": [
        "5b1b3fb240cdb822d2bdcdcb8767b219c247527f"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 13:09:04 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:09:04 2026 +0200"
      },
      "message": "[ty] Refresh uv project metadata in the background (#27933)\n\n## Summary\n\nThis refactors the project\u0027s `uv workspace metadata` call to run in the\nbackground, the same as for scripts. This ensures that the watch command\nand the LSP remain responsive and show a progress indicator while uv is\nrunning.\n\nIt should also make it easier to run `uv workspace metadata --sync` for\nprojects. Although, that does require some work so that we can surface\nprogress from the initial uv call (during project discovery) and not\nhave the LSP blocked until the command completes.\n\nThis PR does not change when we run `uv workspace metadata`. That is, it\ndoesn\u0027t add logic to detect when uv needs to rerun. The same as before,\n`uv workspace metadata` is only called when the `pyproject.toml`\nchanged. It isn\u0027t run when a `uv.toml` or the virtual environment\nchanges. We should do so, but this should be its own PR.\n\nThis PR enables `TY_UV\u003d1 ty check --watch`\n\n## Review\n\nI recommending reviewing this PR commit by commit and with whitespace\nchanges disabled.\n\n\n## Test Plan\n\nLSP:\n\n\n\nhttps://github.com/user-attachments/assets/8c1ab9df-8209-44d5-b0b2-ba06cb824625\n\n\n\n1. Simulate a long `uv metadata` command (e.g. when `uv sync` is holding\nthe lock for multiple seconds). You can see the progress report\n2. Changes to a `pyproject.toml` trigger a refresh\n\nCLI\n\n\nhttps://github.com/user-attachments/assets/b3cde596-c7f5-4f2e-880b-d3885c02dcb8"
    },
    {
      "commit": "5b1b3fb240cdb822d2bdcdcb8767b219c247527f",
      "tree": "d08844eb2861bce94b95a840988b70c82c61a73b",
      "parents": [
        "c7fe174832331a3ff6c12d8216badcb32ca52d80"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 13:09:03 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 13:09:03 2026 +0200"
      },
      "message": "[ty] Synchronize saved script metadata on open (#27873)\n\n## Summary\n\nty supports in-memory changes, but uv always reads the script\u0027s content\nfrom disk. This discrepancy could lead to ty skipping a sync when,\nbetween opening a file and saving it, the script metadata block wasn\u0027t\nchanged, but it changed compared to what was saved on disk when the file\nwas opened.\n\nThe root cause of this issue is that ty computes the script-fingerprint\nfrom the current file content (which may include in-memory changes).\nInstead, it should compute the fingerprint as it is seen on disk.\n\nI considered a few different approaches:\n\n* Support reading the persistent content in `ruff_db`: Add a new\n`persisted_source_text` query, track the persisted `File` revision and\nthe in-memory revision separately. `ScriptsEnvironments` can then\ncompute the fingerprint based on the persisted source text.\n* Use `system.read_text` in `ScriptsEnvironments`. I disregarded that\nquickly because we proactively trigger the synchronization of all\nscripts in a few places and we rely on this bailing quickly if the files\nare unchanged (we don\u0027t want to read each script to bail).\n* Introduce a new `Force` sync mode and use it in `didSave`. \n\nI\u0027d pick the first approach if we had multiple frontends that supported\nin-memory changes. But, given that it\u0027s only the LSP, I started with the\n`Force` sync mode, and I almost opened a PR for it. During the final\ncleanups, I realized that there\u0027s a much simpler LSP-only fix (the CLI\ndoesn\u0027t have this problem).\n\n\nWe don\u0027t need a `persisted_source_text` query if we schedule the script\nsynchronization before we set the Script\u0027s in-memory content in\n`didOpen`. That is, first trigger the synchronization, then open the\nfile and load its in-memory content. That\u0027s all we need.\n\nFixes astral-sh/ty#4250. \n\n## Test Plan\n\n**Before**\n\nReload VS Code with unsaved changes, hit save. Note how the script block\nisn\u0027t synced (because we didn\u0027t make any changes to it)\n\n\n\nhttps://github.com/user-attachments/assets/cd050b94-08a3-4c61-ba14-4a3f1b7a1e48\n\n**This PR**\n\nSame sequence, but the sync runs.\n\n\n\nhttps://github.com/user-attachments/assets/c8acc477-7666-4411-a9b4-df33328ccb7e"
    },
    {
      "commit": "c7fe174832331a3ff6c12d8216badcb32ca52d80",
      "tree": "ae806f2938935670d37745405d6014db2fce37e6",
      "parents": [
        "754d633bd7261a412303b935d47c12d0c1477976"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 12:45:52 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:45:52 2026 +0200"
      },
      "message": "[ty] Fix Goto definition in script dependencies (#27747)\n\n## Summary\n\nEach script with inline metadata (PEP 723) gets its own virtual\nenvironment with its own dependencies. This is to guarantee isolation.\n\n\nBefore for this PR, go to definition only worked when resolving an\nimport directly in a script. E.g. `from mypyp_primer.projects import\nget_projects`, clicking on projects resolved successfully. However, all\ngo to definition requests within `mypy_primer.projects` failed (unless\nthe main project had `mypy_primer` installed as a dependency).\n\n\nThe root cause of this was in `db.program_file(db, file)`. The method\u0027s\nresponsibility is to resolve the most likely program to use for `file`.\nToday, `db.program_file(db, file)` only handled two cases:\n\n* It\u0027s a first-party and it has inline metadata (PEP 723). Use the\nscript\u0027s `Program`. This is why clicking on `mypy_primer.projects`\nworked from within the script\n* Otherwise, use the `Project`\u0027s program\n\n\nBecause of that, any script dependency used the `Project`\u0027s `Program`\ninstead of the script\u0027s.\n\n\nThis PR extends `db.program_file(db, file)` with a heuristic for\nthird-party files (it\u0027s important that `db.program_file(db, file)` is\naccurate for first-party files, for third-party files, the best we can\ndo is \"guess\"). That is, iterate over all known `Program`\u0027s and use the\n`Program` for which file appears on the search paths. Use `Project` as\nthe fallback (still the best guess if everything else fails).\n\n\nThis operation isn\u0027t expensive but it also isn\u0027t cheap. However, we only\ncall `db.program_file` at API boundaries. That is, we mainly call it\nonce for each LSP request. There are a few where the method is called\nmore than once (2 or three times, constant factor), but these are all\nfirst-party files, for which `db.program_file` keeps using the\nfast-path. That\u0027s why I don\u0027t think it\u0027s justified to Salsa cache this\nresolution. But we can if this turns out to be a performance bottleneck\nin the future.\n\nCloses https://github.com/astral-sh/ty/issues/4253\n\n## Test Plan\n\n\nhttps://github.com/user-attachments/assets/5cff9f33-cb54-46d9-9f33-82f1ee63999b\n\n\nAdded language-server integration coverage and ran the affected test\nsuites and Clippy."
    },
    {
      "commit": "754d633bd7261a412303b935d47c12d0c1477976",
      "tree": "5663000e3ca84d8a5310ea223547f1905ae33794",
      "parents": [
        "49af4051805f2c8c1700e8c4842f43c9b5e09ec7"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 12:45:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:45:51 2026 +0200"
      },
      "message": "[ty] Highlight PEP 723 script metadata as TOML (#27746)\n\n## Summary\n\nThis PR adds support for TOML syntax highlighting in PEP 723 inline\nmetadata blocks by emitting TOML specific tokens during semantic\nhighlighting. I know, this wasn\u0027t really something we planned on adding\nbut I got annoyed by starring at large gray blocks and it was too easy.\n\nThis approach is relatively straightforward, and has the advantage that\nit works for all editors. However, it also comes with limitations:\n\n* Parentheses matching and completions don\u0027t work\n* It can\u0027t easily support inserting `# ` on enter (when adding a new\nline)\n* No support for go to def, hover etc (we could add them, but it becomes\na bit more questionable whether this is in ty\u0027s scope)\n\nTo support these features, we\u0027d have to add an embedded grammar to\nty-vscode (and other editors). I think this is still something worth\nconsidering, and the approach taken here doesn\u0027t prevent us from doing\nthis. That\u0027s why I decided to go with this easier and cross-editor\nimplementation for now.\n\nThe emitted tokens are inspired by the grammars of VS Code toml\nextensions.\n\nNote: I\u0027m only aware of sublime supporting semantic highlighting of\ninline metadata. I don\u0027t think pyrefly or pylance do.\n\n## Test Plan\n\n\u003cimg width\u003d\"1245\" height\u003d\"603\" alt\u003d\"Screenshot 2026-08-14 at 11 07 41\"\nsrc\u003d\"https://github.com/user-attachments/assets/120785d5-18a9-478e-af65-77819446b2d2\"\n/\u003e"
    },
    {
      "commit": "49af4051805f2c8c1700e8c4842f43c9b5e09ec7",
      "tree": "3eb0c5f7634c4349b33a06588df35f447c15f367",
      "parents": [
        "4c8d1971bc0c0ba68c09ea85432ed7f2073853f2"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 12:45:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 12:45:51 2026 +0200"
      },
      "message": "[ty] Synchronize PEP 723 script environments in the language server (#27619)\n\nThis completes https://github.com/astral-sh/ty/issues/4183 across all\nsurfaces by integrating the uv script environment synchronization into\nthe language server.\n\nAs explained in earlier PRs. The main challenge in the LSP are:\n\n* Low latency: We don\u0027t want a uv synchronization to block unrelated\nrequests (for other files) or requests that don\u0027t need the initialized\nvirtual environment (go to def and semantic tokens already work decently\nwithout an initialized virtual environment)\n* Avoid flickering diagnostics: We want to avoid diagnostics\ndisappearing and reappearing. Specifically, diagnostics should not\ndisappear when you declare a new dependency only to then reappear when\nthe synchronization is complete. Instead, keep using the old virtual\nenvironment while syncing the new environment so that completions,\ndiagnostics, semantic tokens etc remain unchanged until the sync is\ncomplete. This gives a much nicer and more consistent experience than\nthrowing away the outdated virtual environment or simply returning empty\ndiagnostics while syncing.\n* The LSP supports in-memory changes, uv does not. This PR avoids this\nissue in most cases but not all. I opened a follow up issue to fix the\none instance that I\u0027m aware of where this could lead to ty skipping a\nsynchronization when it should instead trigger a resync. I have ideas of\nhow to fix this, it just requires touching a few core infrastructure\npieces, which would unnecessarily bloat this PR (and I think the case is\nuncommon enough that we can ship an initial version with this known\nbug).\n\n\nThe script support hooks in to the following notifications:\n\n* `didOpen`: Trigger a sync when the user opens a file in the\nbackground. Use an empty virtual environment while the sync is running.\nSkip diagnostics to avoid showing false-positives (we don\u0027t want to show\na bunch of unresolved import errors).\n* `didSave`: Trigger a resync to update the virtual environment if the\ninline metadata changed\n* `didChangeWatchedFiles`: Trigger a resync for files that changed on\ndisk (that aren\u0027t open) but that ty analyzed before (e.g. when using\nworkspace diagnostics).\n\nThe notable omission is `didChange`. I decided not to sync on change.\nMainly because uv doesn\u0027t provide a way to pass the file content via\nstdin. But also because syncing on every change might be a bit\nexcessive. This is something we can reconsider once uv supports syncing\nin-memory changes.\n\n\nCloses https://github.com/astral-sh/ty/issues/4183 (this entire stack)\n\n\n## Opt In\n\nThis PR adds a new `experimantal.useUv` server setting. It has three\nmodes:\n\n* `off`: default, today\u0027s behavior, ty doesn\u0027t use uv to sync script\nenvironments\n* `scripts`: Use uv to sync scripts, but not to retrieve project\nmetadata\n* `on`: Use all supported uv integrations (the same as `scripts` for\nnow)\n\n\nI expect that we\u0027ll iterate on the setting options, but it roughly is\nthe same as `TY_UV`, just for the LSP. It\u0027s also likely that this\nsetting will make it into ty\u0027s persistent configuration. But I decided\nto defer that, because a persistent setting has a higher cost and I\ndon\u0027t know where to add this setting (new `uv` section or is it\n`environment.use_uv` or something else)?\n\n## Test plan\n\n\nhttps://github.com/user-attachments/assets/2782ac93-1ea4-44ff-bc8b-a22ff33c89a7\n\n\nhttps://github.com/user-attachments/assets/8dabd518-0985-4fa5-adb2-24ced78c0469"
    },
    {
      "commit": "4c8d1971bc0c0ba68c09ea85432ed7f2073853f2",
      "tree": "e6baf002fe38e61c530079fc81f4c3df53899902",
      "parents": [
        "ad6cf1776e1124d7649ca152bd1be8bc3bbea56e"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri Aug 28 11:53:05 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 11:53:05 2026 +0200"
      },
      "message": "[ty] Run instrumented benchmarks on Depot (#28126)"
    },
    {
      "commit": "ad6cf1776e1124d7649ca152bd1be8bc3bbea56e",
      "tree": "c7f9c07d74128548dab5405dbe68a4aa4ec3978c",
      "parents": [
        "17761270a5f92dd37c2902bea566cae07ab4a392"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Fri Aug 28 10:51:03 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 10:51:03 2026 +0100"
      },
      "message": "[ty] Preserve list literal positions in starred unpacking (#28061)\n\n## Summary\n\nFix false-positive `invalid-assignment` diagnostics when unpacking list\nliterals, and consolidate the sequence matching, expansion, and\ncapture-list inference used for tuples and lists:\n\n```python\nfirst: int\nfirst, *rest \u003d [1, \"wrong\"]\n```\n\nPreviously, unpacking the inferred `list[int | str]` lost the positions\nof the individual elements and checked `int | str` against the\nannotation on `first`. Preserving the literal\u0027s positions gives\n`Literal[1]` for `first` and `list[str]` for `rest`.\n\nThe implementation shares the underlying operations across their\nconsumers:\n\n- `Tuple::unpack` matches fixed and variable source sequences to fixed\nand starred targets. Assignment unpacking, tuple resizing, and\nsequence-pattern analysis use this matching logic.\n- `TupleBuilder\u003cT, V\u003e` generalizes tuple construction and provides\nshared concatenation through `concat_with`. Tuple inference can retain\nsymbolic `TypeVarTuple` segments, while assignment unpacking can retain\nsource expressions.\n- A shared literal-expansion traversal serves assignment inference,\ntuple-expression inference, and source-expression lookup for\ndiagnostics. It preserves nested literal positions and known prefixes\nand suffixes around expansions without assigning persistent fixed-length\ntypes to mutable lists.\n- Starred assignments and sequence-pattern captures use the same helper\nto infer their new lists, sharing literal and tuple-size promotion rules\nwith ordinary mutable-collection inference.\n\nList literals now receive the same length-mismatch diagnostics and\nunknown-type recovery as tuples. Starred captures from tuples also\nfollow mutable-list promotion rules, including `list[Unknown]` for empty\ncaptures. The existing large-tuple precision limit counts elements\ncontributed by literal expansions, and its widening is preserved through\nnested unpacking.\n\n## Out of scope: contextual inference for unpacking\n\nUnpacking does not currently use annotations on assignment targets as\ntype context when inferring the right-hand side. This is a pre-existing\nlimitation for both tuple and list right-hand sides. For example, these\nvalid assignments still produce false positives:\n\n```python\nfrom typing import Literal\n\nvalue: list[Literal[1]]\nvalue, other \u003d ([1], 0)  # Inferred list[int] is rejected.\nvalue, other \u003d [[1], 0]  # Inferred list[int] is rejected.\n```\n\nIn both cases, the nested list is inferred without the target\u0027s\nannotation. Fixing this missing contextual inference is out of scope for\nthis PR."
    },
    {
      "commit": "17761270a5f92dd37c2902bea566cae07ab4a392",
      "tree": "c6b748adb1e72053e092021b3b82c99246a986ca",
      "parents": [
        "553409a3cc740ab2378bbe72b3963ae06efed99f"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Thu Aug 27 17:03:22 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 17:03:22 2026 -0700"
      },
      "message": "[ty] Preserve short-circuit reachability for direct conditions (#28082)\n\nty can incorrectly treat a branch guarded by an impossible compound\ncondition as reachable when an operand has potentially mutable\ntruthiness. For example, `if value and False:` always skips its body in\nCPython; the first falsy operand selects the control-flow path\nimmediately, it doesn\u0027t fully evaluate `value and False` and then\nre-evaluate the truthiness of the result. Saving `value and False` to a\nvariable and then testing the saved object can instead call\n`value.__bool__` twice and get different answers.\n\nDistinguish expressions consumed directly as conditions from expressions\nthat produce values. Evaluate direct boolean and conditional expressions\nstructurally, and retain the combined truthiness of chained comparisons\neven when a comparison returns an arbitrary object. Operands with types\nequivalent to `Never` cannot produce a result; preserve that distinction\nfrom ambiguous truthiness while composing conditions. Value contexts\ncontinue to allow repeated truthiness checks to disagree.\n\nTo limit retained memory, comparison truthiness uses sparse overrides in\noptional inference metadata, stored as boxed slices. Ordinary comparison\nchains use the truthiness of their inferred value type, and root\nchained-comparison conditions reuse expression inference without a\nseparate condition-analysis memo. Cycle widening preserves overrides\nwhen needed to keep the effective truthiness monotonic. Tracking\noperands that cannot produce a result adds only transient evaluation\nstate.\n\nPrerequisite for #28045, which addresses astral-sh/ty#4380.\n\n## Ecosystem results\n\nFive expected diagnostic removals across pwndbg, pytest, and Spark; no\nadditions. Each comes from a branch that is unreachable under the\nchecked platform or declared types. See the [Codex ecosystem\nanalysis](https://github.com/astral-sh/ruff/pull/28082#issuecomment-5434341797)\nfor details and minimized reproducers.\n\n## Test plan\n\n- Mdtests cover compound conditions, nested boolean operations and\n`not`, conditional expressions, and chained comparisons across branches,\nloops, assertions, comprehension filters, and match guards.\n- Reachability assertions use revealed variable types, including\nreachable saved results, assignment expressions, nested value\nexpressions, and call arguments whose truthiness can be tested again.\n- Cases with operands narrowed to `Never`, aliases and unions of aliases\nof `Never`, and type variables bounded by `Never` assert branch\nreachability using unrelated variables and check precise\nconditional-expression result types.\n- Additional cases cover unreachable loop assignments and conditional\nexpressions in stringified `Annotated` metadata.\n- A Rust invariant test covers sparse comparison-truthiness overrides\nappearing or disappearing across inference-cycle results, including\npreservation of previous-only overrides."
    },
    {
      "commit": "553409a3cc740ab2378bbe72b3963ae06efed99f",
      "tree": "0fb8e10c57f7116de0756fafaa01a951346573d3",
      "parents": [
        "ce3849bced5e1abcba43e911ce0a4997d290e210"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Thu Aug 27 16:53:01 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 16:53:01 2026 -0700"
      },
      "message": "[ty] Preserve parentheses when removing redundant casts (#28092)\n\nRemoving a redundant `typing.cast` could discard parentheses needed for\nmultiline expressions, introducing syntax errors or silently changing\narithmetic results. Preserve the argument\u0027s existing parentheses and\nenclosed comments, and add grouping when a multiline argument relies on\nthe call\u0027s parentheses for line continuation.\n\nThe shared parentheses lookup now checks for an actual closing token so\nthat constructing a fix for an unfinished call does not panic.\n\nFixes https://github.com/astral-sh/ty/issues/4399.\n\n## Test plan\n\n- Added mdtests for multiline conditional and arithmetic expressions,\nincluding a line break before an operator that must not become a\nseparate statement.\n- Covered reordered keyword arguments and comments inside argument\nparentheses.\n- Added unit coverage for unterminated casts with no final newline, with\nand without a trailing comment."
    },
    {
      "commit": "ce3849bced5e1abcba43e911ce0a4997d290e210",
      "tree": "668408165c48885a516f34e8b06ff0759f034875",
      "parents": [
        "f50f825d42578b713ca11accfea486ecb749634b"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Thu Aug 27 16:18:40 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 16:18:40 2026 -0700"
      },
      "message": "[ty] Preserve Final variance in stubs (#28119)\n\nA generic class whose type parameter appears only in a `Final` attribute\nin a stub was incorrectly inferred as invariant. The stub declaration\nalso creates a binding, which variance inference treated as a separate\nmutable occurrence of the type parameter without its `Final` qualifier.\n\nThis change resolves each symbol in the class body through the existing\nsymbol lookup, combining its declarations and bindings before variance\ninference. This preserves the declared qualifiers and correctly infers\ncovariance.\n\nAddresses the `Final`-in-stubs portion of astral-sh/ty#4407. The\nseparate recursive receiver issue reported there remains unresolved.\n\n## Test plan\n\nThe variance mdtests cover stub attributes annotated as `Final[T]`, both\nwithout an initializer and with an ellipsis placeholder. Both forms\nallow covariance and reject reverse subtyping. A writable stub attribute\nis checked in both directions to confirm that it remains invariant."
    },
    {
      "commit": "f50f825d42578b713ca11accfea486ecb749634b",
      "tree": "773556d20c6d81a4b84b7dfa03a3db8dfc1d40ea",
      "parents": [
        "88b8c7a504caf06387cc8697618820e9a0be2d23"
      ],
      "author": {
        "name": "Gore Williams",
        "email": "88089185+gorewilliams@users.noreply.github.com",
        "time": "Fri Aug 28 01:15:11 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 22:15:11 2026 +0000"
      },
      "message": "[ty] Apply mixin specialization when validating enum members (#28101)\n\n## Summary\n\nFixes astral-sh/ty#4403.\n\nAn enum that inherits a data-type mixin as a specialized generic alias\n(`Mixin[str]`) reported every\none of its members as incompatible with the mixin\u0027s `__new__`, whatever\nthe member value or the\nparameter types were:\n\n```py\nclass GenericMixin[T]:\n    if TYPE_CHECKING:\n        def __new__(cls, value: T) -\u003e Self: ...\n\nclass Bad(GenericMixin[str], StrEnum):\n    A \u003d \"a\"  # error: [invalid-assignment]\n```\n\n`inherited_user_defined_mixin_new` walked the MRO and reduced each base\nto its statement-defined\nclass literal before looking up `__new__`, which discarded the\nspecialization carried by a generic\nalias base. The `__new__` it returned still had the mixin\u0027s type\nparameters free, so the implicit\n`cls: type[Self]` had upper bound `Mixin[T@Mixin]` - a bound the enum\nclass does not satisfy.\nInference then failed on the synthesized `cls` argument before the\nmember value was ever considered,\nwhich is why even a mixin whose `__new__` accepts `*args: object`\nrejected its members.\n\nThis keeps the specialization while walking the MRO and projects the\nfunction through it with\n`apply_optional_owner_specialization_to_member`, which also rewrites the\nretained synthetic `Self`\ndomain. Plain `apply_specialization` is not enough: it substitutes the\nannotations but leaves\n`Self`\u0027s upper bound unspecialized, and the false positive survives.\n\nGenuine mismatches are still reported - wrong member type, wrong\nspecialization, wrong tuple arity -\nand the `info` line now renders the specialized signature:\n\n```\ninfo: Expected compatible arguments for `def __new__[Self](cls, value: str) -\u003e Self`\n```\n\nrather than one naming a free typevar (`value: T@GenericMixin`).\n\nThis is unrelated to the \"generic enum\" case from astral-sh/ty#2416:\nthese enums are not generic, and\n`invalid-generic-enum` still fires on a truly generic enum.\n\n## Test Plan\n\nNew `mdtest/enums.md` section, \"Generic data-type mixin `__new__`\",\ncovering a specialized mixin\nbase, specialization through an intermediate generic base, a\nmulti-parameter mixin checked against a\ntuple payload, and a mixin whose `__new__` ignores its type parameter\nentirely - each with both an\naccepted and a rejected member.\n\n---------\n\nCo-authored-by: Carl Meyer \u003ccarl@astral.sh\u003e"
    },
    {
      "commit": "88b8c7a504caf06387cc8697618820e9a0be2d23",
      "tree": "3afe89679364fb2645e5fe13bd5930cafe1c42e1",
      "parents": [
        "3f5e494bd24671d346d3ebbae49c0be275d77e26"
      ],
      "author": {
        "name": "Jelle Zijlstra",
        "email": "jelle@openai.com",
        "time": "Thu Aug 27 14:35:55 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 17:35:55 2026 -0400"
      },
      "message": "[`flake8-datetimez`] Reject `tzinfo\u003dNone` for datetime bounds (`DTZ901`) (#28022)\n\nOnly exempt replace calls with a non-None timezone argument, since\npassing None leaves datetime.min and datetime.max naive.\n\nFixes #28013."
    },
    {
      "commit": "3f5e494bd24671d346d3ebbae49c0be275d77e26",
      "tree": "41a6b33b7b2f61f9a718106cc0679633c3deea74",
      "parents": [
        "9e4938c4a60bed3e87a11ee1e1db1bd23f4d964a"
      ],
      "author": {
        "name": "Shunsuke Shibayama",
        "email": "45118249+mtshiba@users.noreply.github.com",
        "time": "Fri Aug 28 02:02:28 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Aug 28 02:02:28 2026 +0900"
      },
      "message": "[ty] Only treat recursive types with unbounded specializations as fallback cycles (#27538)\n\n## Summary\n\nFixes\nhttps://github.com/astral-sh/ruff/pull/26503#discussion_r3607065111\n\nApproach #26989 didn\u0027t work. This would be a more legitimate approach.\n\nIn #26503, regardless of what the recursive type was, if it appeared\nagain with a different specialization during the visit of\n`CycleDetector`, there was a risk that the visit would not stop, so a\n`Cycle` state was returned immediately.\n\nThis is OK for the growing pattern, but misses the saturating pattern,\nas shown below.\n\n```python\ntype Growing[T] \u003d T | Growing[list[T]]\n\ntype Saturating1[T, U] \u003d T | Saturating1[U, T]\ntype Saturating2[T] \u003d tuple[T] | tuple[T, Saturating2[int]]\ntype Saturating3[T] \u003d tuple[T, Saturating3Helper[list[T]]]\ntype Saturating3Helper[U] \u003d tuple[U, Saturating3[int]]\n```\n\nThink of this as a graph problem. The type parameter of each definition\nis used as a node, and the following edges are created from the type\nparameter of the recursive reference.\n\n* Direct: Type parameters are passed as is.\n* Nested: A type parameter is passed inside another type.\n* Edgeless if no type parameters appear\n\nIf there is even one nested edge in the directed cycle containing the\nroot formal type argument, it is determined that the specialization can\nincrease infinitely.\n\ne.g.\n\n```python\ntype A[X, Y] \u003d A[list[Y], X]\n# A.Y -\u003e A.X  Nested\n# A.X -\u003e A.Y  Direct\n# \u003d\u003e growing\n```\n\n```python\ntype Left[A, B, C, D] \u003d Left[B, C, D, None]\n# Left.B -\u003e Left.A  Direct\n# Left.C -\u003e Left.B  Direct\n# Left.D -\u003e Left.C  Direct\n# \u003d\u003e saturating\n```\n\n```python\ntype A[X, Y] \u003d A[list[Y], None]\n# A.Y -\u003e A.X  Nested\n# \u003d\u003e saturating (no cycle)\n```\n\n## Test Plan\n\nmdtest updated"
    },
    {
      "commit": "9e4938c4a60bed3e87a11ee1e1db1bd23f4d964a",
      "tree": "59317933880d83efcf3676594f80475f3b2496d4",
      "parents": [
        "aad0e909ef1390f4b2a3ba8aa0a67fb8ea5cbacd"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu Aug 27 12:09:07 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 12:09:07 2026 -0400"
      },
      "message": "Bump 0.16.5 (#28110)"
    },
    {
      "commit": "aad0e909ef1390f4b2a3ba8aa0a67fb8ea5cbacd",
      "tree": "46925334a2bcf93d4a8421eb1a29cce965ccada4",
      "parents": [
        "5fdab73c5052350400c36b08c5d7710210343bc4"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu Aug 27 11:26:35 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:26:35 2026 -0400"
      },
      "message": "Allow rules without codes (#28049)\n\nSummary\n--\n\nThis PR allows adding rules that only belong to categories and only have\nnames rather than requiring\na linter group and code.\n\nThe first non-empty commit uses `Rule::name` instead of\n`Rule::noqa_code` throughout the linter tests, causing a huge number of\nfile renames without changes.\n\nThe second commit updates `FixTable` from a mapping of `SecondaryCode -\u003e\n(name, fix_count)` to\n`DiagnosticId -\u003e (Option\u003cSecondaryCode\u003e, fix_count)`. This doesn\u0027t\nreally change its usage but\ninvolves minor diffs to a substantial number of lines. It also allows us\nto drop some of the special\n`hashbrown` handling and the `hashbrown` dependency in `ruff_linter`,\nwhich is nice.\n\nThe third commit contains the bulk of the change, including the macro\nchanges needed to support\n`map_codes` entries like that for the `pytest-fixture-autouse` rule in\nthe fourth commit:\n\n```rust\n() \u003d\u003e rules::ruff::rules::PytestFixtureAutouse,\n```\n\nThese changes are pretty mechanical overall, just handling cases where\nthe linter or noqa code could\nbe missing, which fortunately seemed pretty straightforward in every\ncase.\n\nThe fourth commit takes advantage of these changes to reinstate `RUF076`\nas our first purely\n`pedantic` rule. I\u0027m happy to drop that if it\u0027s too controversial or\nopens too many questions about\nthe previously-removed rule and its code, but I thought it was nice to\nhave a motivating example. We\nmoved this rule from `preview` to `removed` because it was too pedantic\nfor the `RUF` category, even\nin preview, so it\u0027s exactly the kind of rule we can support now with the\ncategories.\n\nFinally, the fifth commit updates the contributing docs to reflect that\nlinters and codes are\noptional. This may need to be adjusted if we back out the `RUF076`\nchange.\n\nTest Plan\n--\n\nExisting tests, plus a few new ones for `pytest-fixture-autouse`"
    },
    {
      "commit": "5fdab73c5052350400c36b08c5d7710210343bc4",
      "tree": "348ed950ccc7a027c9926e6b313bc1df7049d6e1",
      "parents": [
        "29c8e5b2d0a46eb7dc4ff11c1b0a0dc5ccea52e4"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu Aug 27 11:26:34 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:26:34 2026 -0400"
      },
      "message": "Update preview default rules and categories (#27877)\n\n## Summary\n\nThese are the last five commits from\nhttps://github.com/astral-sh/ruff/pull/27666 that update the preview\ndefault selectors based on the new categorization:\n- add current preview rules to the default selectors\n- add third-party rules to the default selectors\n- add preview rules stabilized in 0.15 to the default selectors (oops)\n- incorporate the DTZ and F406 changes from\nhttps://github.com/astral-sh/ruff/issues/27213\n- adjust 4 categories that genuinely differ between my previous and\ncurrent categorizations\n\n## Test Plan\n\nUpdated default rules snapshot"
    },
    {
      "commit": "29c8e5b2d0a46eb7dc4ff11c1b0a0dc5ccea52e4",
      "tree": "f444438f9e443795a2ef2f49d85f72b9f8be4a22",
      "parents": [
        "50a4d7fd106603a5616b01ac3bef3306252b248f"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu Aug 27 11:26:34 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:26:34 2026 -0400"
      },
      "message": "Document rule acceptance guidelines (#27910)\n\nSummary\n--\n\nThis PR includes the contributor-facing documentation for the new\ncategories, as well as updated\nrule acceptance guidelines built atop them.\n\nI think we could also update our rule request template based on some of\nthese guidelines, but I\u0027ll save that for a follow-up once these are\nsolidified.\n\nTest Plan\n--\n\nFuture rule requests\n\n---------\n\nCo-authored-by: Micha Reiser \u003cmicha@reiser.io\u003e"
    },
    {
      "commit": "50a4d7fd106603a5616b01ac3bef3306252b248f",
      "tree": "456c3664e9fd2476649bd1bb19af365b79183bf7",
      "parents": [
        "ada87950ea188f882f69b7bd6e2213a9696e3ee2"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu Aug 27 11:26:33 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:26:33 2026 -0400"
      },
      "message": "Document the new category selectors (#27906)\n\nSummary\n--\n\nThis includes the user-facing updates to our linter docs and selection\nsettings to accompany #27666.\n\nTest Plan\n--\n\nA few existing snapshot updates but mostly users reading this\n\n---------\n\nCo-authored-by: Micha Reiser \u003cmicha@reiser.io\u003e"
    },
    {
      "commit": "ada87950ea188f882f69b7bd6e2213a9696e3ee2",
      "tree": "95eed433591c4afd5a1a8c9266322290f73ec19a",
      "parents": [
        "d8947238863b61922bfc83f07edcc697c1cc07c0"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu Aug 27 11:26:33 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:26:33 2026 -0400"
      },
      "message": "Introduce category selectors (#27666)\n\n## Summary\n\nThis PR introduces clippy-style rule categories that can be used\nalongside existing linter-based categories in preview. The categories\nhave a lower `specificity` than linter groups, meaning that `select \u003d\nUP` and `ignore \u003d correctness` will not ignore `UP` correctness rules.\nIn general this leads to more rules being enabled than the alternative\nwe considered where categories and linter groups had equal precedence,\nwhich makes it seem like a safer default. We can revisit this if it\nproves to be unintuitive, or we can add a Cargo-style `priority` field\nin the worst case.\n\nI\u0027ve tried to organize the commits such that this can be reviewed\ncommit-by-commit and could also be split into multiple PRs. The first\ntwo commits update the `ViolationMetadata` macro to allow providing a\n`category` argument and add such arguments for all rules. This part\ncould be a standalone change without exposing the categories to users\nbut still depends on reaching consensus on all rule categories. The\nsecond pair of commits allows using the new categories as selectors\nalongside existing selectors, including some precedence tests with other\nselector types.\n\nThis mostly addresses #1774, but there are some sub-issues there to\nresolve before closing the issue.\n\n## Test Plan\n\nNew CLI tests and a new mdtest suite around rule precedence"
    },
    {
      "commit": "d8947238863b61922bfc83f07edcc697c1cc07c0",
      "tree": "ec88c689e7d0afde67b084e20beead7fd88439e6",
      "parents": [
        "2685fdebbcf9938736fed8c45886a629f9c99a06"
      ],
      "author": {
        "name": "Malkin0xb8",
        "email": "42589044+malkin0xb8@users.noreply.github.com",
        "time": "Thu Aug 27 16:21:04 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 16:21:04 2026 +0100"
      },
      "message": "[ty] Infer lambda parameters through callable type aliases (#28109)"
    },
    {
      "commit": "2685fdebbcf9938736fed8c45886a629f9c99a06",
      "tree": "1112e2b7c4b04575b9653623f859b6d77d75e34b",
      "parents": [
        "efcffd2178ce62e9951a53d4c50cadc225a0cfec"
      ],
      "author": {
        "name": "BitWeaver",
        "email": "hello.bitweaver@gmail.com",
        "time": "Thu Aug 27 17:20:02 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 16:20:02 2026 +0100"
      },
      "message": "[ty] Narrow functional enum members in `\u003d\u003d` and `match` (#28103)"
    },
    {
      "commit": "efcffd2178ce62e9951a53d4c50cadc225a0cfec",
      "tree": "0a124815ab083baa046973081d1d7080fe24acb8",
      "parents": [
        "eb780488037504e11f145ed778654fd8a825028b"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Thu Aug 27 15:39:14 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 13:39:14 2026 +0000"
      },
      "message": "[ty] Intersection simplifications with subtype-related generic specializations (#26880)\n\n## Summary\n\nIn this PR we implement two kinds of intersection simplifications which\nhelp other parts of type inference. The patterns involved mostly come up\nin (strict mode) `isinstance`/`TypeIs` narrowing.\n\nFirst, we implement the following two (previously derived)\nsimplifications which complement the already-implemented `Invariant[P] \u0026\nInvariant[Any] \u003d Invariant[P]` simplification:\n\n```\nCo[P] \u0026 Co[Any] \u003d Co[P \u0026 Any]\nContra[P] \u0026 Contra[Any] \u003d Contra[P | Any]\n```\n\nSecond, we derive and implement a new kind of simplification, which can\nbe summarized as:\n\n```\nBase[P] \u0026 Top[Sub[Any]] \u003d Sub[P]               (Base and Sub have the same variance)\nBase[P] \u0026 Top[Sub[Any]] \u003d Top[Sub[P \u0026 Any]]    (Base: covariant, Sub: invariant)\nBase[P] \u0026 Top[Sub[Any]] \u003d Top[Sub[P | Any]]    (Base: contravariant, Sub: invariant)\n```\n\nFor covariant generic classes, a special case of this is `Base[P] \u0026\nSub[object] \u003d Sub[P]`.\n\nThe covariant/invariant case here allows us to simplify intersections\nlike `Sequence[int] \u0026 Top[list[Any]]` to `Top[list[int \u0026 Any]]`. This\nintersection can result from a (strict mode) `isinstance(.., list)`\ncheck on an object of type `Sequence[int]`. The following figure shows a\ngraphical depiction of the involved subtyping relations:\n\n\u003cimg width\u003d\"600\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/a00766f6-5049-4e15-8477-4f1c15a7c000\"\n/\u003e\n\nThe resulting type `Top[list[int \u0026 Any]]` preserves the important\nfeatures of the full intersection: When iterating over that type, we get\nelements of `Top[int \u0026 Any] \u003d int`, which is what we expect by starting\nfrom `Sequence[int]`. However, if we try to `append` an element to that\nlist, since the element type appears in contravariant position, we get\n`Bottom[int \u0026 Any] \u003d Never`. This means that we cannot append any\nelements to that list. This is expected, since we could be dealing with\na `list[int]` or a `list[bool]`, or a `list[Literal[False]]`, and so on.\nSo whatever we want to append needs to be compatible with all of those\ntypes, which is impossible.\n\ncloses https://github.com/astral-sh/ty/issues/3676 (was previously\nclosed by non-strict generic narrowing, now it works with strict mode as\nwell)\ncloses https://github.com/astral-sh/ty/issues/4191 (similar, works in\nstrict mode now as well)\ncloses https://github.com/astral-sh/ty/issues/512 (simplifies to\n`Child[int]` now)\n\n## Out of scope\n\nThree things have been intentionally left out of this PR:\n\n- This PR doesn\u0027t actually solve\nhttps://github.com/astral-sh/ty/issues/1824 (but does solve several\nissues that were closed as duplicates of this, see above). I\u0027ve realized\nthat the proposed simplification for the original problem in that issue\nrequires an unsound extension of what is proposed here (since it\ninvolves structural types, and the results here only apply to nominal\ntypes). I will defer that to a follow-up PR to evaluate it in isolation.\n- The results above can be easily generalized to the following relations\non *gradual* types (`P` fully static):\n\t```\n\tBase[P] \u0026 Sub[Any] \u003d Sub[P \u0026 Any]  (covariant base)\n\tBase[P] \u0026 Sub[Any] \u003d Sub[P | Any]  (contravariant base)\n\tBase[P] \u0026 Sub[Any] \u003d Sub[P]        (invariant base)\n\t```\nWe do not implement these generalized simplifications here, but could\nconsider doing so in a follow-up. I generally expect these intersections\nto come up way less often (maybe in `TypeIs` narrowing with\nuser-functions, since we don\u0027t top-materialize them), so it\u0027s unclear if\nthat\u0027s worth doing, even if it looks like a nice addition.\n- Intersections involving `type[..]`. I will look into this in a\nfollow-up.\n\n## Ecosystem\n\nCodex: Overall, the ecosystem impact supports the intended\nsimplifications: six false positives disappear, six Spark true positives\nbecome visible under our nominal assumptions, and one ignore becomes\nredundant. Another 38 changes rewrite types in existing diagnostics\nwithout adding or removing errors. One Spark true positive is lost: more\nprecise narrowing triggers an existing ambiguous-overload fallback to\nUnknown, hiding an invalid list-plus-tuple operation. This is a\ndownstream precision regression caused by [existing overload-resolution\nlimitations](https://github.com/astral-sh/ty/issues/665), not an\nincorrect intersection simplification.\n\n## Conformance\n\n~~The change in the conformance tests exposes a pre-existing inference\nproblem. If the generics solver could infer the correct type for the\n`is_two_element_tuple` type, we would not see this change.~~ (fixed in a\npreparation PR)\n\n## Test Plan\n\nNew Markdown tests"
    },
    {
      "commit": "eb780488037504e11f145ed778654fd8a825028b",
      "tree": "cde330fe27830c9217fafcf7b0680ea7c4c9ca92",
      "parents": [
        "97060ba557e31ff2f3d75d32dc93ef48d0c7ffa2"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Thu Aug 27 15:37:15 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 13:37:15 2026 +0000"
      },
      "message": "[ty] Bump ecosystem-analyzer for HTML escaping (#28104)\n\nPick up https://github.com/astral-sh/ecosystem-analyzer/pull/180 to fix\nHTML escaping in reports."
    },
    {
      "commit": "97060ba557e31ff2f3d75d32dc93ef48d0c7ffa2",
      "tree": "c29cad13481f72f07e009d350193613e9b35579c",
      "parents": [
        "8b807f65cff1fe54bd53a38d920f959b2dffae65"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Thu Aug 27 11:24:48 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:24:48 2026 +0100"
      },
      "message": "[ty] Preserve inferred types for unknown class-decorator results (#28067)"
    },
    {
      "commit": "8b807f65cff1fe54bd53a38d920f959b2dffae65",
      "tree": "d33c865eba7edc4d2dc21b28e481f5bed4399a67",
      "parents": [
        "b4531166f345c2867805107fea99980fb1e78453"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Thu Aug 27 11:21:32 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 11:21:32 2026 +0100"
      },
      "message": "[ty] Fix compiler warning seen in `cargo nextest run` invocations locally (#28096)"
    },
    {
      "commit": "b4531166f345c2867805107fea99980fb1e78453",
      "tree": "0938e4d27e3c00c5263faa53fe50bb6b5d84d89b",
      "parents": [
        "b52fe1ba252f81ea50581e074b6d7e77dca9dea3"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Wed Aug 26 21:05:57 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 27 04:05:57 2026 +0000"
      },
      "message": "[ty] Avoid panics on incomplete protocol type parameter lists (#28088)\n\nty can panic when a generic protocol definition has an incomplete type\nparameter list. The autofix for `invalid-generic-class` assumes that the\nlist contains both brackets, which can produce an inverted range or\nsplit a Unicode identifier when the closing bracket is missing.\n\nCheck the delimiters before comparing the type parameters with the\n`Protocol` subscript. Incomplete lists still produce diagnostics, but do\nnot receive this autofix. Targeted Hawk expectations preserve the public\nsaturating arithmetic helpers in `TextSize`.\n\nFixes astral-sh/ty#4397.\n\n## Test plan\n\nAdd mdtests for an empty, unclosed type parameter list and an unclosed\nlist containing a Unicode name. Both cases produce diagnostics without\npanicking. Existing protocol tests retain coverage for the autofix on\ncomplete type parameter lists."
    },
    {
      "commit": "b52fe1ba252f81ea50581e074b6d7e77dca9dea3",
      "tree": "7cc74a4fba6d820a8215ab643200a053750080ed",
      "parents": [
        "ceb9d316058af194d03588cd249f1ad5ca526b8d"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Wed Aug 26 23:38:01 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 23:38:01 2026 +0100"
      },
      "message": "[ty] Add a test suite for upcoming rules detecting always-truthy and always-falsy boolean tests (#28030)\n\n## Summary\n\nThis PR adds a test suite for two rules that I\u0027ve been working on:\n`redundant-condition` and `redundant-condition-strict`. The proposed\nbehaviour is that `redundant-condition` will be enabled by default and\n`redundant-condition-strict` will be disabled by default. In my\n[implementation](https://github.com/astral-sh/ruff/pull/27634) currently\n(which is not part of this PR), `redundant-condition` has 411 ecosystem\nhits, while `redundant-condition-strict` has 2,972.\n\nThe two rules both flag a common problem: boolean tests that are\naccidentally always truthy or always falsy. For example, a common error\nin Python is to do this:\n\n```py\nimport random\n\ndef f() -\u003e bool:\n    return random.choice([True, False])\n\ndef g():\n    if f:  # oops, forgot the parentheses, so this will always be true!\n        ...\n```\n\nMany of the tests here outline exemptions that must be applied to these\nrules in order to avoid a prohibitive number of false positives,\nhowever. For example, if the user has configured their Python version as\nbeing 3.14 when checking their code with ty, `if sys.version_info \u003e\u003d (3,\n14)` will always be inferred by ty as evaluating to `Literal[True]` --\nbut flagging that condition with a diagnostic wouldn\u0027t be helpful\nbehaviour. My implementation therefore exempts any conditions where a\nsubexpression in that condition can be identified as referring to one of\nfour symbols: `sys.version_info`, `sys.platform`, `os.name` or\n`typing.TYPE_CHECKING`. It also follows (recursively) the definitions of\nnames and attributes used in the boolean condition through to their\noriginal definitions, to examine whether any names or attributes used in\nthe condition were defined in relation to one of those four constants.\nIf so, no diagnostic will be reported on the condition.\n\nTogether, these two rules cover all of mypy\u0027s `truthy-function`,\n`truthy-bool` and `redundant-expr` error codes, as well as overlapping\npartially with mypy\u0027s `unreachable` error code."
    },
    {
      "commit": "ceb9d316058af194d03588cd249f1ad5ca526b8d",
      "tree": "5915a1f90e2d93d90600c08d4239694cad078c72",
      "parents": [
        "9775a671183f5eed4228f2d59ff7a159e0da45d2"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Wed Aug 26 14:37:28 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 14:37:28 2026 -0700"
      },
      "message": "[ty] Factor function-level work out of pytest fixture request lookup (#28073)\n\n\u003c!--\nThank you for contributing to Ruff/ty! To help us out with reviewing,\nplease consider the following:\n\n- Does this pull request include a summary of the change? (See below.)\n- Does this pull request include a descriptive title? (Please prefix\nwith `[ty]` for ty pull\n  requests.)\n- Does this pull request include references to any relevant issues?\n- Does this PR follow our AI policy\n(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?\n--\u003e\n\n## Summary\n\nIn anticipation of [\"find references\" for pytest\nfixtures](https://github.com/astral-sh/ruff/pull/28075), which\nenumerates possible fixture requests across every parameter of a\nfunction, this PR factors the shared, function-level work in\n`fixture_request_for_parameter` into `FixtureRequestContext`.\n\nThis lets the follow-up construct one context per function and reuse it\nfor each candidate parameter, rather than repeatedly calling:\n\n- `fixture_declaration`, `is_collected_test`, and\n`is_unittest_test_case` to determine whether the function can request\nfixtures.\n- `infer_definition_types` to retrieve the function type.\n- `mock_patch_count` to determine how many parameters are supplied by\n`unittest.mock.patch`.\n\nThe existing `fixture_request_for_parameter` entry point now constructs\na context and delegates to it, preserving behavior for existing callers.\nThis is a behavior-preserving refactor.\n\n## Test Plan\n\nThis is a refactor that relies on existing test coverage (although I\nhave backfilled a new test for some pre-existing behaviour).\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "9775a671183f5eed4228f2d59ff7a159e0da45d2",
      "tree": "34cbdbba4f0baae998208079c71b4892a68db891",
      "parents": [
        "50a3eae8ee503b0bd6d6ad77c989c9ce5722896b"
      ],
      "author": {
        "name": "Douglas Creager",
        "email": "dcreager@dcreager.net",
        "time": "Wed Aug 26 17:13:02 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 17:13:02 2026 -0400"
      },
      "message": "[ty] Extract `SequentMap` and `PathAssignments` into dedicated modules (#28080)\n\nThis file\u0027s getting big. This is a pure refactoring that splits things\nup a bit."
    },
    {
      "commit": "50a3eae8ee503b0bd6d6ad77c989c9ce5722896b",
      "tree": "693aade188fb37f6a672b3381167af218d7e7f2c",
      "parents": [
        "007157d47915cc126d996ccdc3e3b28f216c221f"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed Aug 26 13:51:16 2026 -0600"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 13:51:16 2026 -0600"
      },
      "message": "Run Hawk in CI (#27372)\n\n## Summary\n\nWe now run Hawk 0.1.13 with Rust 1.98.0 in CI, following the pattern\nused by uv. The configuration preserves intentional public APIs and\ngenerated interfaces while checking all configured production binaries\nand workspace non-production targets with `-D warnings`.\n\nThe accompanying cleanup narrows internal visibility, removes unused\nbenchmark classification state and accessors, and records intentional\npublic interfaces as audited Hawk expectations. The resulting\nconfiguration reports zero Hawk findings across the configured targets."
    },
    {
      "commit": "007157d47915cc126d996ccdc3e3b28f216c221f",
      "tree": "3af9ec4d7a2646a0fbe39a49d659bbdffaedd13f",
      "parents": [
        "0eeb97d5958ff5587330592e39d2ba93957c5a29"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Wed Aug 26 11:17:52 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 11:17:52 2026 -0700"
      },
      "message": "[ty] Preserve correlated generic-call inference (#28043)\n\nGeneric-call inference currently unions each type variable\u0027s solutions\nindependently. This loses correlations between alternative\nspecializations and whether a solve relied on a budget-exhaustion\nfallback.\n\nRetain correlated, context-aligned alternatives and their completeness\nin `TypeVarInference`, alongside the union-merged mapping used by\nexisting consumers. Argument checking, return types, constructors,\npartial signatures, and IDE signature help continue to use the explicit\nmerged projection for now; this prepares call inference to consume\nalternatives in a future PR, while intentionally preserving the current\nvisible behavior.\n\nPreserve incomplete outcomes through contextual preferences and\npromotion, bound alternative storage, and keep diagnostic and\ncompatibility recovery mappings separate from correlated solutions.\n\nPart of astral-sh/ty#3557.\n\n## Test plan\n\nRust tests cover correlated and incomplete solution families, unsolved\ndefaults, context ordering, budget exhaustion, recovery mappings,\nindependent merged and per-path cleanup, expanding cycles, and\npreservation through real call binding and type promotion. Some of these\ntests should be promoted to mdtests once the behavior they test is\nexposed in a visible way in a future PR.\n\nAn mdtest covers overloads that exchange their input and output types,\npreserving the current union-merged return tuple and documenting the\neventual intersection of return types."
    },
    {
      "commit": "0eeb97d5958ff5587330592e39d2ba93957c5a29",
      "tree": "6deeb88f5dda854222fa58a2946ce769d79be142",
      "parents": [
        "92e326495250226bb6bc7b1e3293b705185a96b9"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Wed Aug 26 11:13:27 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 11:13:27 2026 -0700"
      },
      "message": "[ty] Avoid unnecessary Salsa queries during pytest fixture lookup (#28071)\n\n\u003c!--\nThank you for contributing to Ruff/ty! To help us out with reviewing,\nplease consider the following:\n\n- Does this pull request include a summary of the change? (See below.)\n- Does this pull request include a descriptive title? (Please prefix\nwith `[ty]` for ty pull\n  requests.)\n- Does this pull request include references to any relevant issues?\n- Does this PR follow our AI policy\n(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?\n--\u003e\n\n## Summary\n\nIn anticipation of [\"find references\" for pytest\nfixtures](https://github.com/astral-sh/ruff/pull/28075), which\nintroduces many more speculative attempts at resolving pytest fixtures,\nthis PR helps preserve efficiency by short-circuiting a few Salsa\nqueries:\n\n- Return from `fixture_declaration` before calling\n`function_known_decorators` when a function has no decorators.\n- Return from `fixture_candidates_from_definition` before calling\n`exists_at_runtime` when the definition kind cannot expose a fixture.\n- Return from `directly_parametrized` before calling\n`function_known_decorators` when the test function has no decorators\n(while still checking parametrization from an enclosing class).\n\nThese are all behavior-preserving early exits.\n\n## Test Plan\n\nThis is a refactor that relies on existing test coverage.\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "92e326495250226bb6bc7b1e3293b705185a96b9",
      "tree": "c8b20cd7df6933b99261c2e68b524b0201f54256",
      "parents": [
        "dfd606ffedbfd7c596f77900adc3bfa5e588321a"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Wed Aug 26 19:06:42 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 19:06:42 2026 +0100"
      },
      "message": "Update args for uv-lock hook in pre-commit config (#28078)\n\n- `--refresh` enforces our uv cooldown period: manual changes to the\nlockfile that violate the cooldown will not actually cause this hook to\nfail without `--refresh`\n- `--check` means that there\u0027s a better error message when the hook\nfails in CI"
    },
    {
      "commit": "dfd606ffedbfd7c596f77900adc3bfa5e588321a",
      "tree": "0e65eac0796e2b1c29c3bcfcd6d137148213ba57",
      "parents": [
        "49e49f5b7535e942d77e8efe34f9083f07d53e98"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Wed Aug 26 19:01:17 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 19:01:17 2026 +0200"
      },
      "message": "[ty] Add Python requirements to uv script test fixtures (#28076)"
    },
    {
      "commit": "49e49f5b7535e942d77e8efe34f9083f07d53e98",
      "tree": "9a9302e4feded749e685cbc52cf470fdca6001a4",
      "parents": [
        "7c4724f0515514d6669dae36d66472bfe72c355c"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed Aug 26 10:54:24 2026 -0600"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 16:54:24 2026 +0000"
      },
      "message": "[ty] Infer variance through nonrecursive protocol references (#28065)\n\n## Summary\n\nFollow-up to #27531.\n\nWe now validate declared protocol variance when member types refer to\nnonrecursive protocols. Previously, any protocol reference disabled the\ncheck, so `marker` hid the variance mismatch in `Source`:\n\n```python\nfrom typing import Protocol, TypeVar\n\nT \u003d TypeVar(\"T\")\n\nclass Marker(Protocol):\n    def ready(self) -\u003e bool: ...\n\nclass Source(Protocol[T]):  # error: [invalid-protocol]\n    def read(self) -\u003e T: ...\n    def marker(self) -\u003e Marker: ...\n```\n\nThe variance guard now checks dependencies by protocol definition and\nuses the same read/write types as variance inference. Explicit receiver\nannotations no longer suppress validation, and unrelated protocol\nmembers no longer bypass invariance inference for writable attributes.\n\nDirect, mutual, and expanding recursive interfaces remain deferred, as\ndo descriptor writes whose accepted values cannot be represented by a\nsingle type."
    },
    {
      "commit": "7c4724f0515514d6669dae36d66472bfe72c355c",
      "tree": "ed182656819c910af2fe51e0573037312c37c9a3",
      "parents": [
        "d5a639442529a457a1023fa639814adc4229b7a6"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Wed Aug 26 07:25:42 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 07:25:42 2026 -0700"
      },
      "message": "[ty] Preserve invariant materialization constraints (#28047)\n\nType inference can lose type-variable bounds when comparing\nmaterializations of invariant generic types. For example, assigning\n`Top[Invariant[Any]]` to `Top[Invariant[T]]` requires both `object \u003c: T`\nand `T \u003c: Never`, so there is no fully static solution for `T`. Skipping\nthese comparisons incorrectly makes the relation appear satisfiable.\n\nPreserve both endpoint constraints during lazy type-variable evaluation.\nMaterialized recursive protocols also check their recursive members,\nwhich can impose bounds absent from their non-recursive members.\n\nWhen a recursive protocol stabilizes at a new specialization, retain its\nnominal constraints if the source and target share an origin and the\npending materializations leave their requirements unchanged. This check\nremains conservative for lazy function signatures, wrapped partial\ncallables, and divergent markers.\n\n## Ecosystem impact\n\nThe three Expression warning rewrites expose an existing solver\nlimitation tracked in\n[astral-sh/ty#3467](https://github.com/astral-sh/ty/issues/3467):\nexistential projection can discard return-type information involving an\nunsolved callback-local type variable. This PR removes an invalid\ninferred relationship that previously masked that limitation, so\n`Parser[Any]` becomes `Unknown` in those warnings. Fixing the solver\nlimitation is outside the scope of this PR.\n\n## Test plan\n\n- Cover exact invariant specializations and impossible top/bottom\ncomparisons involving `Any`.\n- Check bounds contributed by recursive consuming methods and recursive\nproperties, including source-only, target-only, and opposite\nmaterializations and incompatible combined bounds.\n- Accept stable recursive specializations while rejecting growing\nincompatible specializations; check that hidden function types, partial\ncallables, and divergent markers cannot establish a no-op\nmaterialization."
    },
    {
      "commit": "d5a639442529a457a1023fa639814adc4229b7a6",
      "tree": "9a9eb45468bf07674185f132a553973a8802db91",
      "parents": [
        "26f4f73ebf55c2d9ac5a8720846bcacf00b08334"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Wed Aug 26 14:21:42 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 14:21:42 2026 +0100"
      },
      "message": "Fix compiler warnings seen in CI jobs (#28066)"
    },
    {
      "commit": "26f4f73ebf55c2d9ac5a8720846bcacf00b08334",
      "tree": "bdc4ec929f36f2f5525f54f77255d545a8ec7906",
      "parents": [
        "f41e26329881c4411f24c5aff701f4629420b0f8"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Wed Aug 26 15:03:09 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 15:03:09 2026 +0200"
      },
      "message": "[ty] Infer tuple type parameters from union arguments (#28062)\n\n## Summary\n\nInfer tuple type parameters from every alternative of a union of tuples:\n\n```python\ndef elements[T](values: tuple[T, ...]) -\u003e tuple[T, ...]:\n    return values\n\ndef f(values: tuple[str, str] | tuple[str, str, str]):\n    reveal_type(elements(values))  # tuple[str, ...]\n```\n\nPreviously, this inferred `tuple[Unknown, ...]`.\n\nThis came up while working on\nhttps://github.com/astral-sh/ruff/pull/26880. The conformance suite\nexample here requires proper type inference for the\n`is_two_element_tuple` function from showing up.\n\n## Ecosystem\n\nTwo new true positives. `CodecOptions` uses an invariant type parameter,\ndespite inheriting from covariant `tuple`. Previously ty inferred\n`Unknown` which prevented the error. mypy/pyright also reject this call.\n\n## Test plan\n\nNew Markdown tests"
    },
    {
      "commit": "f41e26329881c4411f24c5aff701f4629420b0f8",
      "tree": "a2eb7565be595210db03d20ed763eb4478af06b2",
      "parents": [
        "a9fa5e59e5ccc57ee3a79d595e97ebccc26aa7cf"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed Aug 26 06:45:42 2026 -0600"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 06:45:42 2026 -0600"
      },
      "message": "[ty] Validate generic protocol variance (#27531)\n\n## Summary\n\nFollow-up to #27534.\n\nWe now validate explicitly declared generic protocol variance against\nits structural interface:\n\n```python\nfrom typing import Protocol, TypeVar\n\nT \u003d TypeVar(\"T\")\n\n\nclass Source(Protocol[T]):  # error: [invalid-protocol]\n    def read(self) -\u003e T: ...\n```\n\nA type variable appearing only in a return position must be covariant,\nwhile writable protocol attributes must remain invariant. We derive both\ndeclared and inferred variance from the same member read/write\ncapabilities already used for structural compatibility, including\ninferred legacy and PEP 695 protocol parameters:\n\n```python\nclass Writable[T](Protocol):\n    _value: T\n\n\ndef overwrite(value: Writable[object]) -\u003e None:\n    value._value \u003d object()\n\n\ndef unsound(value: Writable[int]) -\u003e None:\n    overwrite(value)  # error: [invalid-argument-type]\n```\n\nFor now, I\u0027ve limited this to the easier cases, so recursive interfaces,\ncustom descriptors with unrepresentable setter domains, generic\ndefaults, and inherited or malformed headers are unchanged."
    },
    {
      "commit": "a9fa5e59e5ccc57ee3a79d595e97ebccc26aa7cf",
      "tree": "9c5cb53cca89f6533c4a9e002addde97b5d72558",
      "parents": [
        "14622e635d4f219f9da68d22c425ac4e7db53e8d"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Wed Aug 26 12:39:00 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 12:39:00 2026 +0200"
      },
      "message": "[ty] Bump ecosystem-analyzer for script dependency preparation (#28058)"
    },
    {
      "commit": "14622e635d4f219f9da68d22c425ac4e7db53e8d",
      "tree": "26e08de9df8233b053baf4745778f38d198ab268",
      "parents": [
        "33b9648a76693c7d98e7ddf795e20401511db5de"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Wed Aug 26 12:10:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 12:10:45 2026 +0200"
      },
      "message": "Respect the pygls version bound in ruff-lsp tests (#28057)"
    },
    {
      "commit": "33b9648a76693c7d98e7ddf795e20401511db5de",
      "tree": "e2874bc3404b76416e641a438daed94defbbd4a3",
      "parents": [
        "2acf96a75fd715ecd619be9b394a613bdb88c80b"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed Aug 26 08:12:56 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 08:12:56 2026 +0200"
      },
      "message": "Update prek dependencies (#28055)"
    },
    {
      "commit": "2acf96a75fd715ecd619be9b394a613bdb88c80b",
      "tree": "47dfcc94f7ed48f10ee7a04c1f355591bef48075",
      "parents": [
        "3d4fd30d2fd5fe0511417fffb133d6dbb28660fd"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed Aug 26 08:12:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Aug 26 08:12:28 2026 +0200"
      },
      "message": "Update dependency astral-sh/uv to v0.12.6 (#28054)"
    },
    {
      "commit": "3d4fd30d2fd5fe0511417fffb133d6dbb28660fd",
      "tree": "b9cf11aeae64733835eb35f55c24c07e4db7daa5",
      "parents": [
        "c7b114b60c5e16b0d4a268f1355db31d29651536"
      ],
      "author": {
        "name": "Kris Kaczor",
        "email": "chris@kaczor.io",
        "time": "Wed Aug 26 07:14:05 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 22:14:05 2026 -0700"
      },
      "message": "[ty] Infer `yield from` send/return types from the iterator returned by `__iter__` (#27987)\n\n## Summary\n\nFixes astral-sh/ty#4368. This is my attempt to fix the issue by using\nAI.\n\n`yield from x` delegates to `iter(x)`, so the value of the expression\n(and the send type that must be compatible with the outer generator)\ncomes from the *iterator* returned by `x.__iter__()`, not from `x`\nitself. Previously we only looked at `x`\u0027s own MRO for\n`Generator`/`Iterator`, so for a class whose `__iter__` is a generator\nfunction we inferred `Unknown` and skipped the send-type check:\n\n```py\nclass Box:\n    def __iter__(self) -\u003e Generator[str, None, int]:\n        yield \"hello\"\n        return 42\n\ndef main() -\u003e Generator[str, None, int]:\n    x \u003d yield from Box()\n    reveal_type(x)  # was: Unknown, now: int\n    y: str \u003d yield from Box()  # now: error[invalid-assignment]\n    return x\n```\n\nThis PR resolves the inner `GeneratorTypes` once in\n`infer_yield_from_expression`: if the operand itself is a\ngenerator/iterator we use that (unchanged behaviour, keeps the protocol\nmaterialization handling), otherwise we call `__iter__` and take the\ngenerator types of the returned iterator. Both the send-type validation\nand the expression\u0027s type now use that. The now-unused\n`generator_send_type` helper is removed.\n\n`yield from` over a plain Iterator now consistently returns `Unknown`,\nsince the `Iterator` type does not specify the return type, and an\niterator implementation could raise `StopIteration` with any value,\nwithout violating any annotated type.\n\n## Test Plan\n\n- New mdtests in `expression/yield_and_yield_from.md` covering the\nissue\u0027s example, a send-type mismatch detected through `__iter__`, and\nthe `Iterator` / list case.\n- `cargo test -p ty_python_semantic`, `cargo clippy -p\nty_python_semantic --all-targets -- -D warnings`, `cargo fmt --check`.\n- Checked the built CLI against the repro from the issue and against a\nreal-world codebase relying on this pattern (effecton); the expected new\ndiagnostic appears and nothing else changes.\n\n---------\n\nCo-authored-by: Carl Meyer \u003ccarl@astral.sh\u003e"
    },
    {
      "commit": "c7b114b60c5e16b0d4a268f1355db31d29651536",
      "tree": "b549625fe9f849597e300062f69dcc9aee7fdda9",
      "parents": [
        "fd393f3628c119068395c274bc162808610b961d"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Aug 25 21:06:40 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 22:06:40 2026 -0600"
      },
      "message": "[ty] Fix TypedDict variance inference (#28052)\n\nTypedDict types currently contribute no constraints to variance\ninference, so a type parameter used only through a TypedDict is treated\nas unused. This can infer the wrong variance for an enclosing generic\nclass.\n\nInfer variance through TypedDict items and explicit extra items,\ncomposing each value type\u0027s variance with its mutability. Class\ndefinitions use the TypedDict schema, including inherited items, instead\nof ordinary attribute heuristics. The implementation reuses class\nspecialization and cycle handling, preserves declared legacy variance,\nand handles functional and synthesized TypedDicts.\n\n## Test plan\n\nAdded mdtests for mutable and read-only items, optional items with\nunderscore-prefixed names, callable item types, specialized inherited\nitems, declared legacy variance, inherited extra items, functional\ndefinitions capturing an enclosing type parameter, and recursive\ndefinitions with both unchanged and expanding type arguments."
    },
    {
      "commit": "fd393f3628c119068395c274bc162808610b961d",
      "tree": "2f94191dcb854d9f357c3b7771d5a155b4c1e0a1",
      "parents": [
        "b58ab7212613978463f6dc9945a3eb3e72e96eb3"
      ],
      "author": {
        "name": "Marcelo Amaral",
        "email": "marceloamaral386@gmail.com",
        "time": "Tue Aug 25 22:29:59 2026 -0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 18:29:59 2026 -0700"
      },
      "message": "[ty] Support tagged union with multiple tags per type (#27984)\n\nFixes https://github.com/astral-sh/ty/issues/4359.\n\n## Summary\n\nty determines whether to narrow the type of a tagged union by checking\nif the tag is of a supported literal type. However, literals with\nmultiple possible values are represented as `Type::Union`, which fails\nthe check.\n\nThis can be fixed by checking that all values in the union are literals.\nNot sure if this is the best way to approach the problem, but it should\nat least provide a general direction.\n\nNote: there is still one behavior that might be unwanted:\n```python\ndef protocol_union_multiple_tags(value: TaggedA | TaggedC1):\n    if value.tag \u003d\u003d \"c\":\n        reveal_type(value)  # revealed: TaggedC1\n    elif value.tag \u003d\u003d 1:\n        reveal_type(value)  # revealed: TaggedC1\n    else:\n        reveal_type(value.tag)  # revealed: Literal[\"a\"]\n        reveal_type(value)  # revealed: TaggedA | TaggedC1\n```\nThe last case could have been just `TaggedA`, but that is out of scope\nfor this PR.\n\n## Test Plan\n\nAdded mdtests for the new supported case.\n\n---------\n\nCo-authored-by: Carl Meyer \u003ccarl@astral.sh\u003e"
    },
    {
      "commit": "b58ab7212613978463f6dc9945a3eb3e72e96eb3",
      "tree": "ccd4081fa3117fea12cb8dfd0c191f0a667e38e9",
      "parents": [
        "c232eb18c1455b31d29e5e47c980d9b575d49e08"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Tue Aug 25 15:37:32 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 15:37:32 2026 -0700"
      },
      "message": "[ty] Add \"Go to Definition\" support for pytest fixtures (#27444)\n\n\u003c!--\nThank you for contributing to Ruff/ty! To help us out with reviewing,\nplease consider the following:\n\n- Does this pull request include a summary of the change? (See below.)\n- Does this pull request include a descriptive title? (Please prefix\nwith `[ty]` for ty pull\n  requests.)\n- Does this pull request include references to any relevant issues?\n- Does this PR follow our AI policy\n(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?\n--\u003e\n\n## Summary\n\n\u003c!-- What\u0027s the purpose of the change? What does it do, and why? --\u003e\nThis adds \"Go to Definition\" support for pytest fixture request\nparameters in tests and fixture functions. It resolves user-authored\nfixtures exposed through modules, imports and reexports, class\nhierarchies, and applicable `conftest.py` files, together with fixtures\nregistered by pytest\u0027s installed core plugins.\n\nThis initial delivery has the following limitations:\n\n- Fixtures exposed only through third-party plugin registration—such as\n`pytest_plugins` or installed `pytest11` entry points—are not\ndiscovered. Explicitly imported fixtures are supported.\n- Fixture dependencies requested by another fixture are resolved from\nthe declaring fixture\u0027s lexical context. pytest instead resolves them\naccording to the test that activates the fixture, so navigation can\ndiffer from runtime:\n\n  ```python\n  import pytest\n\n  @pytest.fixture\n  def dependency(): ...\n\n  @pytest.fixture\ndef consumer(dependency): ... # Go to Definition selects the module\nfixture.\n\n  class TestOverride:\n      @pytest.fixture\n      def dependency(self): ...\n\n      def test_consumer(self, consumer): ...\n      # At runtime, consumer receives TestOverride.dependency.\n  ```\n\n- Fixture discovery and request classification cover common static\npytest patterns, but not all custom collection, configuration, or\ndecorator patterns.\n- The special `request` fixture has no source declaration, so it has no\nfixture navigation target.\n\nThis PR changes only \"Go to Definition\". Fixture-aware \"Go to\nDeclaration\", references, rename, hover, document highlights, type\ninference, completion, and inlay hints remain unchanged here.\n\n## Test Plan\n\nSee included tests. Please note that most behaviours are tested directly\nwithin `ty_python_semantic`, so all that\u0027s necessary at this layer are\nsimple integration tests.\n\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "c232eb18c1455b31d29e5e47c980d9b575d49e08",
      "tree": "08b5063683105069a06d259787575555db4797cd",
      "parents": [
        "cc932fa706be64f4afc5053ffb8ddfb836b7e32a"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Aug 25 15:15:31 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 15:15:31 2026 -0700"
      },
      "message": "[ty] Preserve bounds of non-literal metaclasses (#28046)\n\nClass-object values can lose their metaclass bound when the metaclass is\nknown as `type[M]` rather than a single class literal. This prevents ty\nfrom recognizing that `type[C]` values are still instances of `type`,\nproducing false errors in exhaustive `type()` matches and incorrectly\naccepting returns as unrelated types such as `int`.\n\nPreserve the bound in `SubclassOfType::to_meta_type` by projecting the\ninferred metaclass to its instance type before rebuilding the metatype.\nThe lookup-only typeshed protocol fallback remains excluded from\ninheritance guarantees.\n\nFixes astral-sh/ty#4370.\n\n## Test plan\n\nAdd a metaclass mdtest using an explicit `type[Meta]` parameter. It\nchecks the preserved `type[Meta]` metatype for subclasses, exhaustive\n`case type()` narrowing, and rejection of an invalid `int` return."
    },
    {
      "commit": "cc932fa706be64f4afc5053ffb8ddfb836b7e32a",
      "tree": "fc24801c3cd2eb341a29123dea4237376a61d5d9",
      "parents": [
        "a7cf72750caeed24d71e587730108e2ff1fc97a4"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Tue Aug 25 15:10:27 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 15:10:27 2026 -0700"
      },
      "message": "[ty] Resolve installed core pytest fixture providers (#27770)\n\n\u003c!--\nThank you for contributing to Ruff/ty! To help us out with reviewing,\nplease consider the following:\n\n- Does this pull request include a summary of the change? (See below.)\n- Does this pull request include a descriptive title? (Please prefix\nwith `[ty]` for ty pull\n  requests.)\n- Does this pull request include references to any relevant issues?\n- Does this PR follow our AI policy\n(https://github.com/astral-sh/.github/blob/main/AI_POLICY.md)?\n--\u003e\n\n## Summary\n\n\u003c!-- What\u0027s the purpose of the change? What does it do, and why? --\u003e\nThis extends the pytest fixture model from\nhttps://github.com/astral-sh/ruff/pull/27540 to resolve fixtures\nsupplied by pytest\u0027s [built-in\nplugins](https://docs.pytest.org/en/stable/reference/fixtures.html).\nPytest makes these fixtures available without an import or a local\n`conftest.py`, so ty currently cannot connect requests such as\n`tmp_path` or `monkeypatch` to their declarations.\n\nty now reads the statically known `default_plugins` registration from\nthe installed `_pytest.config`, resolves those plugins from the same\npytest installation, and searches them in pytest\u0027s precedence order\nafter project providers. This preserves local fixture shadowing and also\nmodels the dynamic `LegacyTmpdirPlugin` registration that supplies the\nlegacy `tmpdir` fixtures. Registries that cannot be determined\nstatically remain unresolved.\n\nThis implementation intentionally supports pytest 8.x and newer; earlier\npytest versions are outside the supported scope.\n\n## Test Plan\n\nSee included tests.\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "a7cf72750caeed24d71e587730108e2ff1fc97a4",
      "tree": "d5d92bda06648d0bc65e4f1831147722ca548929",
      "parents": [
        "373a60fcc176335ca5baf361db10c0752f5c69c0"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Tue Aug 25 22:28:43 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 22:28:43 2026 +0100"
      },
      "message": "[ty] Add a dedicated missing-slot diagnostic (#28039)\n\n## Summary\n\nEmit `missing-slot` when an instance assignment targets a declared\nattribute but the instance has neither a matching slot nor an instance\ndictionary. Previously, this used `unresolved-attribute`.\n\nThis lets users affected by generated stubs such as protobuf\u0027s disable\nthis check without disabling other `unresolved-attribute` diagnostics.\n\nFixes astral-sh/ty#4375."
    },
    {
      "commit": "373a60fcc176335ca5baf361db10c0752f5c69c0",
      "tree": "c89267c5c8912ec05fc6d3edd0ebfe506e242909",
      "parents": [
        "aa5278159b486fab789ec7c89e4dc0425d78b2ff"
      ],
      "author": {
        "name": "Jelle Zijlstra",
        "email": "jelle@openai.com",
        "time": "Tue Aug 25 14:00:58 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 17:00:58 2026 -0400"
      },
      "message": "[`flake8-async`] Detect blocking generic HTTP requests (`ASYNC210`) (#28024)\n\nRecognize requests.request and httpx.request alongside their\nmethod-specific helpers when checking for blocking HTTP calls in async\nfunctions.\n\nFixes #28015."
    },
    {
      "commit": "aa5278159b486fab789ec7c89e4dc0425d78b2ff",
      "tree": "c057bb36a7f40abf896259260ace37505647c805",
      "parents": [
        "d9a492329c64fc8d6cca5b5dcb4cac955ef0915d"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Aug 25 13:58:15 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 13:58:15 2026 -0700"
      },
      "message": "[ty] Ignore inconsistent binding decorators on overloads (#28036)\n\nWhen an overload set applies `@staticmethod` inconsistently, ty reports\nthe invalid declaration but also treats every overload as static. This\nleaves `self` unbound on instance overloads and causes a cascading\n`no-matching-overload` error.\n\nIf a decorator\u0027s application to an overload is invalid, and we report\nthat error, it makes more sense to ignore that invalid decorator than to\nimplicitly apply it to every overload, as we did before.\n\nIgnore a `@staticmethod` or `@classmethod` binding decorator unless it\napplies consistently across every overload and any implementation. Keep\nthe `invalid-overload` diagnostic, per-definition receiver annotations\nand body checking, and implicit special-method binding. Preserve the\nexisting fallback when overload discovery encounters a cycle.\n\nFixes astral-sh/ty#4388.\n\n## Test plan\n\n- Extend overload mdtests with imported staticmethod and classmethod\nstubs in both declaration orders, instance and class access, and\nreceiver-incompatible overloads.\n- Cover mismatches involving implementations and verify that\nconsistently applied decorators retain their behavior, including when an\nunrelated overload error is present."
    },
    {
      "commit": "d9a492329c64fc8d6cca5b5dcb4cac955ef0915d",
      "tree": "a5bcddf63f4cb759b9439191dce9d320b32a15b9",
      "parents": [
        "76e909d6a4f7829a41ed49d2af6da8407c32ddb9"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Aug 25 13:57:21 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 13:57:21 2026 -0700"
      },
      "message": "[ty] Reject incompatible constructor context for variadic packs (#27966)\n\nAn expected return type could determine a variadic constructor\u0027s type\narguments even when its actual arguments were incompatible. In\nparticular, returning `A()` from a function annotated with `A[*Ts, int]`\ncould infer the expected specialization instead of `A[()]`, hiding an\ninvalid return.\n\nCompare the complete variadic argument tuple with the contextual\nspecialization and retry inference without that context when they are\nincompatible.\n\nBuilds on #27943 for fixed-tuple rejection and pack-preserving length\nnarrowing, with #27950 and #27957 providing missing-unpack recovery and\ngradual tuple assignability.\n\nFixes astral-sh/ty#4343.\n\n## Test plan\n\n- Cover missing and extra arguments under concrete contexts, and empty\nand nonempty constructor calls under symbolic contexts with and without\na required suffix.\n- Preserve compatible concrete widening and symbolic pack forwarding.\n- Preserve concrete and symbolic context for gradual arguments,\nincluding nested tuples and aliases of `Any`.\n- Reject incompatible fixed lengths and gradual boundaries."
    },
    {
      "commit": "76e909d6a4f7829a41ed49d2af6da8407c32ddb9",
      "tree": "09036e940dc53d6a78d0ecbddf1b34ab22402717",
      "parents": [
        "82c2fe71ea3f5e4739f8684f71d53ad81d1cdbfd"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Aug 25 13:57:20 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 13:57:20 2026 -0700"
      },
      "message": "[ty] Reject fixed tuples for non-inferable TypeVarTuples (#27943)\n\nOn main, a function returning `tuple[*Ts]` can wrongly return an\nunrelated fixed-length tuple without an error. For example, returning\n`()` from a variadic-identity function caused ty to infer a nonempty\ntuple at call sites even though the runtime result was an empty tuple.\n\nOnly expand a non-inferable `TypeVarTuple` target when the source tuple\nis variable-length. Fixed tuples now reach the ordinary type-variable\nrejection instead of repeating the same tuple comparison and succeeding\nthrough the recursion guard. Inference and explicit constraint\nassumptions keep their existing behavior.\n\nLength narrowing retains the original symbolic pack alongside the\nrefined tuple shape, so checking a tuple\u0027s length does not prevent\nreturning it as `tuple[*Ts]`.\n\nDepends on #27950 to avoid cascading errors after a missing\n`TypeVarTuple` unpack.\n\nFixes astral-sh/ty#4342.\n\n## Test plan\n\nAdded mdtests for empty and nonempty fixed returns, annotated\nassignments, matching tuple prefixes and suffixes, subtyping, and fixed\ntuples containing `Any` or `Never`. Constraint-implication coverage\nchecks that an applicable assumption permits the relationship while\nincompatible elements and unconstrained packs remain rejected.\n\nLength-narrowing coverage checks empty and one-element tuples, fixed\nprefix/suffix elements, and aliases. A required `Never` element\ndocuments the remaining tuple-disjointness limitation tracked in #27920."
    },
    {
      "commit": "82c2fe71ea3f5e4739f8684f71d53ad81d1cdbfd",
      "tree": "32edd716488ea75a143309a08da0ac7bd7830525",
      "parents": [
        "da2e1251fa6c878784b264942a980d2c9bffc3fe"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Aug 25 13:57:20 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 25 13:57:20 2026 -0700"
      },
      "message": "[ty] Recover bare TypeVarTuples in tuple annotations (#27950)\n\nA bare `TypeVarTuple` in a tuple annotation represents an unknown number\nof elements, not a single element. Recover it as `*tuple[Unknown, ...]`\nwhile preserving the missing-unpack diagnostic. This avoids imposing an\nincorrect fixed length and preserves known surrounding elements:\n`tuple[Ts]` becomes `tuple[Unknown, ...]`, while `tuple[int, Ts, str]`\nbecomes `tuple[int, *tuple[Unknown, ...], str]`.\n\nRecovery is local to the bare pack\u0027s position. Enclosing tuple and\n`type` annotations retain their structure, and ordinary `Unknown`\nelements keep their fixed positions. Quoted annotations, `typing.Tuple`,\nand implicit tuple aliases use the same recovery. Independent sibling\nerrors are still reported, and recovered packs do not introduce\nadditional multiple-unpack diagnostics.\n\nDepends on #27957 for gradual tuple assignability to symbolic packs.\n\n## Test plan\n\n- Add PEP 695 mdtests for direct and homogeneous tuple annotations;\nfixed elements surrounding a bare pack; nested tuple and `type`\nboundaries; ordinary `Unknown` elements; quoted annotations; independent\nsibling diagnostics; and recovery alongside other variadic elements.\n- Add legacy `TypeVarTuple` mdtests for the constructor/assignment\nexample, `typing.Tuple`, implicit aliases used as expressions and\nannotations, and preservation of valid union alternatives in a\nhomogeneous tuple element."
    }
  ],
  "next": "da2e1251fa6c878784b264942a980d2c9bffc3fe"
}
