)]}'
{
  "log": [
    {
      "commit": "2965c98cb5b4fbce65aa9a271da1c38b6730ba80",
      "tree": "b373cba0dbd2ddddea7fa334d6b8488ff0f19486",
      "parents": [
        "6b4082451d8bdcead0859eac9ad75351ad63e3bd"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Tue Jun 02 17:02:03 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 17:02:03 2026 -0700"
      },
      "message": "[ty] treat union-bound typevars like unions for possibly-missing-attribute (#25561)\n\n## Summary\n\nTreat bounded `TypeVar`s like their upper bound when deciding whether a\n`possibly-missing-attribute` should be promoted to\n`unresolved-attribute`. We missed this case in\nhttps://github.com/astral-sh/ruff/pull/23042, and then we disabled\n`possibly-missing-attribute` by default, so on main we are silent by\ndefault when accessing an attribute that is missing on some elements of\na typevar upper-bound union.\n\n## Testing\n\nAdded mdtest."
    },
    {
      "commit": "6b4082451d8bdcead0859eac9ad75351ad63e3bd",
      "tree": "85326c67cdacdc86cdd9b22759decb82f07fb333",
      "parents": [
        "d7f1a5b72ba98776126389f25da85ef3c58755f8"
      ],
      "author": {
        "name": "Jack O\u0027Connor",
        "email": "oconnor663@gmail.com",
        "time": "Tue Jun 02 15:12:32 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 15:12:32 2026 -0700"
      },
      "message": "[ty] include nested `global`/`nonlocal` bindings in type inference (#25387)\n\nHere\u0027s the basic `nonlocal` case:\n\n```py\ndef f():\n    x \u003d 42\n    def g():\n        nonlocal x\n        x \u003d \"hello\"\n    g()  # NOTE: Inference doesn\u0027t ask whether `g` is actually called here. We assume it could be called.\n    reveal_type(x)  # revealed: Literal[42, \"hello\"]\n```\n\n`global` declarations work similarly:\n\n```py\nx \u003d 0\ndef f():\n    global x\n    x +\u003d 1\n# NOTE: As above, the fact that `f` isn\u0027t actually called doesn\u0027t affect inference.\nreveal_type(x)  # revealed: int\n```\n\nHere\u0027s the general behavior, which is deliberately a bit unsound:\n\n- After the close of each function or class scope, we synthesize\nbindings that refer to all the nested `global` or `nonlocal` bindings\ntransitively within that scope.\n- These \"synthetic nested binding definitions\" don\u0027t shadow existing\nbindings, and they aren\u0027t shadowed by subsequent bindings, modeling the\nfact that nested function bodies can run \"at any time\".\n- Using a variable `x` in scope that doesn\u0027t bind it ends up walking to\nits defining scope and seeing its \"public type\", which includes all\nnested bindings in every scope. But if you bind `x` locally, that\nshadows the public type. On the other hand, synthetic bindings\ncorresponding to scopes nested within the current scope still count as\nlocal. (So generally parent and sibling scope bindings will get\nshadowed, but not child scope bindings.)\n\nFor more of the details here see the new mdtests, particularly\n\"Visibility of `nonlocal` bindings from nested and sibling scopes\"."
    },
    {
      "commit": "d7f1a5b72ba98776126389f25da85ef3c58755f8",
      "tree": "6aac819af147c17077a7302ac58f3bbf9cbe6107",
      "parents": [
        "5ae991932b22326902f49176663190f21f11a022"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Tue Jun 02 15:56:10 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 21:56:10 2026 +0200"
      },
      "message": "[ty] Preserve slice-bound types in subscript inference (#25446)\n\n## Summary\n\nPrior to this change, when a slice bound was not an integer literal,\nboolean literal, or `None`, we inferred a bare `slice[Any, Any, Any]`.\nThis erased enough information that subscription checking accepted\ninvalid slices even when `__getitem__` requires `SupportsIndex | None`\nbounds:\n\n```python\ndef get(values: list[int], start: float) -\u003e list[int]:\n    return values[start:]  # error: [invalid-argument-type]\n```\n\n(This may have been waiting on\nhttps://github.com/astral-sh/ruff/commit/cb58704e085fe7e70d72842294575de47e47bead?)\n\nWe now preserve the inferred type of each provided slice bound in\n`slice[...]`, so ordinary argument checking diagnoses invalid bounds for\nbuilt-in sequences and user-defined `__getitem__` methods with typed\nslice parameters.\n\nCloses https://github.com/astral-sh/ty/issues/3547."
    },
    {
      "commit": "5ae991932b22326902f49176663190f21f11a022",
      "tree": "081d29c3d70d5b64b957cbde813c45e3c450d819",
      "parents": [
        "f83e48cabc2971108870bcddcc81ed7674598476"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Tue Jun 02 15:46:09 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 21:46:09 2026 +0200"
      },
      "message": "[ty] Normalize dynamic class literals in cycle recovery (#25558)\n\n## Summary\n\nA dynamically created class can capture the previous value of a\nloop-carried variable in its members or bases. Prior to this change, we\ntreated class literals as atomic during recursive type normalization, so\nthe class\u0027s interned identity kept growing on each inference cycle and\nSalsa eventually panicked after too many cycle iterations.\n\nCloses https://github.com/astral-sh/ty/issues/3627."
    },
    {
      "commit": "f83e48cabc2971108870bcddcc81ed7674598476",
      "tree": "ed32ae6a5521fdc15d8889c405cc9c4f290c00a1",
      "parents": [
        "bc52b711c0c9f85080632b613e85416c70527fe7"
      ],
      "author": {
        "name": "Parafee41",
        "email": "parafee_2041@qq.com",
        "time": "Wed Jun 03 03:18:39 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 12:18:39 2026 -0700"
      },
      "message": "[`pylint`] Avoid syntax errors in invalid character replacements in f-strings before Python 3.12 (`PLE2510`, `PLE2512`, `PLE2513`, `PLE2514`, `PLE2515`) (#25544)\n\n## Summary\n\nFixes #18816.\n\nWhen a raw control character appears inside an f-string replacement\nfield or format spec, replacing it with an escape sequence can introduce\ninvalid pre-3.12 syntax. This keeps the diagnostic, but suppresses the\nfix for interpolated f-string/t-string elements when the target version\nis older than Python 3.12. Literal f-string text and Python 3.12+\ntargets keep the existing fix behavior.\n\n## Test plan\n\n- `cargo test -p ruff_linter --features test-rules invalidcharacter`\n- `cargo test -p ruff_linter --features test-rules\nrules::pylint::tests::invalid_characters_pre_py312`\n- `cargo fmt --check`\n- `git diff --check`\n\n---------\n\nCo-authored-by: Brent Westbrook \u003cbrentrwestbrook@gmail.com\u003e"
    },
    {
      "commit": "bc52b711c0c9f85080632b613e85416c70527fe7",
      "tree": "1332fab4ef798fe20215380fe483dd1548c12025",
      "parents": [
        "8aaa13eceb7c332399b494140b17f2ffebdd02be"
      ],
      "author": {
        "name": "Anish Giri",
        "email": "161533316+anishgirianish@users.noreply.github.com",
        "time": "Tue Jun 02 12:00:45 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 17:00:45 2026 +0000"
      },
      "message": "[`pyupgrade`] Avoid converting `format` calls with more kinds of side effects (`UP032`) (#25484)\n\n## Summary\n\nFixes part of #15874: UP032 unsoundly converted str.format calls that\nrepeat a side-effecting argument. The check only\ncaught calls, so a repeated d[k] or walrus slipped through. Switch it to\ncontains_effect.\n\n## Test Plan\n\nNew mdtest at crates/ruff_linter/resources/mdtest/pyupgrade/f-string.md.\n\n---------\n\nCo-authored-by: Brent Westbrook \u003cbrentrwestbrook@gmail.com\u003e"
    },
    {
      "commit": "8aaa13eceb7c332399b494140b17f2ffebdd02be",
      "tree": "c6527c4acbf17057aa75469b94335f76aa188242",
      "parents": [
        "d08cde09a057e34b87e117d5c4f6825ee6421525"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Tue Jun 02 16:09:41 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 16:09:41 2026 +0100"
      },
      "message": "Run ecosystem-analyzer on all mypy_primer projects (#25549)\n\nUpdate to the latest version of ecosystem-analyzer. This version does\naway with the `--projects-old` and `--projects-new` flags, instead\nrunning ecosystem-analyzer on all mypy_primer projects. If panics on\npanicking projects are fixed, that will be celebrated by\necosystem-analyzer; if new panics are introduced, that will be announced\nloudly by ecosystem-analyzer, but we don\u0027t need to worry about adding\nthe project to a bad.txt file anymore. We also don\u0027t need to worry\nanymore about keeping these lists up to date with the upstream list of\nprojects in mypy_primer itself."
    },
    {
      "commit": "d08cde09a057e34b87e117d5c4f6825ee6421525",
      "tree": "60650ad777a0309007781aa5143d68d41917d702",
      "parents": [
        "4828decefa910db8d920c7c384c57b5332a8eb9f"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Tue Jun 02 09:30:59 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 06:30:59 2026 -0700"
      },
      "message": "[`eradicate`] Avoid flagging `ruff:ignore` comments as code (`ERA001`) (#25537)\n\nSummary\n--\n\nFixes #25535 by treating `ruff:ignore` and `ruff:file-ignore` comments\nin the same way as `ruff:enable` and `ruff:disable` comments.\n\nTest Plan\n--\n\nNew tests based on the issue"
    },
    {
      "commit": "4828decefa910db8d920c7c384c57b5332a8eb9f",
      "tree": "284c35bbc51d556de1f9d5f3a9c3e3ca0cf6707a",
      "parents": [
        "06dd02bcf7167c4ae530e7ce30a66dfabef8160f"
      ],
      "author": {
        "name": "RoyS",
        "email": "roy676564@gmail.com",
        "time": "Tue Jun 02 09:00:07 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 07:00:07 2026 +0100"
      },
      "message": "[ty] ty: fix typos in completion.rs comments (#25532)\n\nSigned-off-by: RoySerbi \u003croy676564@gmail.com\u003e"
    },
    {
      "commit": "06dd02bcf7167c4ae530e7ce30a66dfabef8160f",
      "tree": "ad0c46279f68db5238895e09c3a27067d1c831a0",
      "parents": [
        "44c2a46470f66904a925803e2f6b3c794c4ce7c7"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Jun 01 21:39:16 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 21:39:16 2026 -0700"
      },
      "message": "[ty] don\u0027t inject Unknown from non-callable elements of intersection call (#25538)"
    },
    {
      "commit": "44c2a46470f66904a925803e2f6b3c794c4ce7c7",
      "tree": "821d1a6eccf3a2503c752fa5f708d9fc089ba360",
      "parents": [
        "83b08f3b94e60eaf1c07cdb0175f63ff9de57e67"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 20:22:59 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jun 02 02:22:59 2026 +0200"
      },
      "message": "[ty] Omit redundant definitions-by-node entries (#25501)\n\n## Summary\n\nThis is a follow-up to #25498 to omit redundant entries from the\nretained mapping between AST nodes and definitions.\n\nLoop-header definitions are internal use-def bindings retrieved through\ntheir `LoopToken`, so we no longer retain mappings from their enclosing\n`while` or `for` statements.\n\nFor non-variadic parameters, we previously retained duplicate mappings\nfrom both the outer `ParameterWithDefault` node and the inner Parameter\nnode to the same definition. This PR consistently uses the inner node as\nthe key and normalizes lookups from the outer node accordingly."
    },
    {
      "commit": "83b08f3b94e60eaf1c07cdb0175f63ff9de57e67",
      "tree": "42171a80f3f6a2fab9b7ddcd19e94e5fc862d2d1",
      "parents": [
        "c9ce22a9585975de284b72984df0f8e7aa2568ac"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Jun 01 16:48:13 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 16:48:13 2026 -0700"
      },
      "message": "[ty] use callable type context to implicitly specialize generic class (#25471)\n\n## Summary\n\nUse a callable type context to inform the implicit specialization of a\ngeneric class.\n\nCloses https://github.com/astral-sh/ty/issues/3577.\n\n## Test Plan\n\nAdded mdtests.\n\n---------\n\nCo-authored-by: Douglas Creager \u003cdcreager@dcreager.net\u003e"
    },
    {
      "commit": "c9ce22a9585975de284b72984df0f8e7aa2568ac",
      "tree": "cb9f72037cbd8e2ef1a63082f330238042ed1cc7",
      "parents": [
        "11723aeb5b9bcdbc25240c9bb04908057c862100"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 19:28:46 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 23:28:46 2026 +0000"
      },
      "message": "[ty] Compact retained definitions by node (#25498)\n\n## Summary\n\nWhen we build the semantic index, we retain a mapping from\ndefinition-producing AST nodes to their definitions. Most nodes have\nexactly one definition, but we currently store every entry in the\nappend-friendly `Definitions` representation used while building the\nindex.\n\nThis PR compacts the mapping when we freeze the semantic index. We store\ncommon single-definition entries directly and reserve exact-size boxed\nslices for uncommon entries with zero or multiple definitions (including\nwildcard imports and synthesized loop headers).\n\n(The primary motivation here is reducing memory.)"
    },
    {
      "commit": "11723aeb5b9bcdbc25240c9bb04908057c862100",
      "tree": "9841a039acf26e5d53b23c35fe4b94caffb654f8",
      "parents": [
        "06e77271bb4522ded6feb45269e86b4a5e8d454c"
      ],
      "author": {
        "name": "Douglas Creager",
        "email": "dcreager@dcreager.net",
        "time": "Mon Jun 01 17:58:58 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 17:58:58 2026 -0400"
      },
      "message": "[ty] Add mdtest extension functions for constraint-set assignability (#25534)\n\nThis adds two new extension methods that were helpful while I was\nreviewing #25471:\n\n- `ConstraintSet.with_detailed_display` causes us to print out the full\nboolean formula of a constraint set. I put this behind a method so that\nwe aren\u0027t tempted to add mdtests that assert on the rendering of a\nconstraint set, since this can change arbitrarily (even across different\nruns of ty).\n\n- `is_constraint_set_assignable_to` lets us test the\n`ConstraintSetAssignability` typing relation, which differs from\n`Assignability` in how it handles comparisons against typevars."
    },
    {
      "commit": "06e77271bb4522ded6feb45269e86b4a5e8d454c",
      "tree": "2cd0f31f7b898913e6cf74fc8f04456e583e8de6",
      "parents": [
        "3a200ce85a602c0eb19639ed696fea7e4a36323c"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 15:54:44 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 21:54:44 2026 +0200"
      },
      "message": "[ty] Consolidate retained use-def definition maps (#25499)\n\n## Summary\n\nWhen we build the use-def map, we retain prior bindings for each\ndefinition and prior declarations for each binding. These were\npreviously stored in two separate hash maps, even though every entry in\nthe declarations map had a corresponding entry in the bindings map.\n\nThis PR consolidates them into a single map. Each retained definition\nstores its prior bindings and, when needed, its\nprior declarations. This removes the duplicated keys and the overhead of\nretaining a second hash table.\n\n(The primary motivation here is reducing memory.)"
    },
    {
      "commit": "3a200ce85a602c0eb19639ed696fea7e4a36323c",
      "tree": "8043a8262fa9d0eae415f485443937b7a40f7944",
      "parents": [
        "bdf9f0022f48adb377b850e9ce9ec9810657194d"
      ],
      "author": {
        "name": "justin",
        "email": "commonmodestudio@gmail.com",
        "time": "Mon Jun 01 15:50:16 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 12:50:16 2026 -0700"
      },
      "message": "[ty] Distinguish `typing.Callable` from `collections.abc.Callable` (#24954)\n\n## Summary\nhttps://github.com/astral-sh/ty/issues/887\n\nThis is a blocker to supporting `Callable()` in class patterns in match\nstatements (discussion\n[below](https://github.com/astral-sh/ruff/pull/24954#discussion_r3171744197)).\n`typing.Callable` is a deprecated alias to `collections.abc.Callable` -\ncalling the former raises a `TypeError` at runtime whereas the latter\ndoesn\u0027t.\n\nTo allow `Callable()` to be used in `match` statements, we need to be\nable to distinguish between the two.\n\nComplication: typeshed defines `collections.abc.Callable` as a re-export\nalias from `typing` (`collections/abc.pyi` -\u003e `_collections_abc.pyi` -\u003e\n`from typing import Callable as Callable`). This required some\nadditional plumbing to pass `collections.abc` through as the known\nmodule (the re-exporting module) vs `typing`\n\n## Test Plan\nNew mdtests\n\n---------\n\nCo-authored-by: Lérè \u003ccontact@lrbr.dev\u003e"
    },
    {
      "commit": "bdf9f0022f48adb377b850e9ce9ec9810657194d",
      "tree": "d6e0e4789c690776c3a31a4fa93deda19523d7b7",
      "parents": [
        "aa76512529ddbfdb36d1be36654e40f5e07fe434"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 15:46:03 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 21:46:03 2026 +0200"
      },
      "message": "[ty] Consolidate AST ID reverse lookup (#25455)\n\n## Summary\n\nConsolidates the per-scope AST ID reverse lookup maps into a single\nfile-level map. IDs are still assigned per scope, preserving stable\nouter-scope IDs while reducing retained map overhead."
    },
    {
      "commit": "aa76512529ddbfdb36d1be36654e40f5e07fe434",
      "tree": "8772d30baabe622af354da5c2ea656c476f348d4",
      "parents": [
        "ec248a28b30f6f20091c3d9b6f31a0ba36f57186"
      ],
      "author": {
        "name": "George Ogden",
        "email": "38294960+George-Ogden@users.noreply.github.com",
        "time": "Mon Jun 01 20:20:33 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 12:20:33 2026 -0700"
      },
      "message": "[`flake8-pytest-style`] Avoid fixes for ambiguous `argnames` and `argvalues` combinations (`PT006`) (#24776)\n\nFixes #24715 and related to #22441\n\n## Summary\n\nThis prevents changes that break code (almost certainly) in rule\n[PT006](https://docs.astral.sh/ruff/rules/pytest-parametrize-names-wrong-type/#pytest-parametrize-names-wrong-type-pt006).\n\nConsider the following examples:\n```python\nimport pytest\n\nTEST_CASES \u003d [...]\n@pytest.mark.parametrize((\"number\",), TEST_CASES)\ndef foo(number): ...\n\ndef generate_test_case():\n    ...\n\n@pytest.mark.parametrize((\"number\",), (generate_test_case(),))\ndef foo(number): ...\n\nsingle_test_case \u003d ...\n@pytest.mark.parametrize((\"number\",), [single_test_case])\ndef foo(number): ...\n```\nBefore this change, the `argnames` would be updated, which would\nprobably break the test. After this change, no fix will be applied.\n\n## Test Plan\n\nI added one test, but mostly fixed existing tests.\n\n---------\n\nCo-authored-by: Brent Westbrook \u003cbrentrwestbrook@gmail.com\u003e"
    },
    {
      "commit": "ec248a28b30f6f20091c3d9b6f31a0ba36f57186",
      "tree": "8d4e95906f8388883568e66731c3663a699d3603",
      "parents": [
        "6b717e8d553a8076f6927894e221064a923aa13a"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Jun 01 11:29:51 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 11:29:51 2026 -0700"
      },
      "message": "[ty] Avoid redundant solve for empty collection context (#25527)\n\n## Summary\n\nAvoid building and solving a `SpecializationBuilder` constraint set for\nan empty collection literal when contextual inference has already\nprovided its complete specialization.\n\nFor this case, directly apply the contextual specialization and return\nthe resulting collection instance.\n\n## Motivation\n\nProfiling pydantic exposed an expensive path for empty collection\nliterals with large contextual types. Even when the context completely\ndetermines the collection type variables and the literal has no elements\nthat could add information, inference continued through constraint\nprojection and solving.\n\nThis is especially expensive for pydantic\u0027s large schema unions.\n\n## Validation\n\nExisting tests pass, no ecosystem diagnostic changes."
    },
    {
      "commit": "6b717e8d553a8076f6927894e221064a923aa13a",
      "tree": "9c9a5b4d963ac46f84256729d76256d14356848b",
      "parents": [
        "b82dfe720b5656acc462390999a5b12d0df42ee0"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Jun 01 11:25:49 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 18:25:49 2026 +0000"
      },
      "message": "[ty] detect recursive expansion in constraint-set solving (#25442)\n\n## Summary\n\nCatch infinite recursive expansion in constraint-set solving.\n\nCloses https://github.com/astral-sh/ty/issues/3578\n\n## Test Plan\n\nAdded test that runs forever before this fix."
    },
    {
      "commit": "b82dfe720b5656acc462390999a5b12d0df42ee0",
      "tree": "05d2dd5e34b8fea49f07241e80640b8e4d6f7077",
      "parents": [
        "099c89fe76117fdee24e910c286dbc4b56301ffa"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Mon Jun 01 08:09:46 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 15:09:46 2026 +0000"
      },
      "message": "[ty] Extend Generator assignability workaround to Python 3.13+ (#25472)\n\n## Summary\nKeep the `Generator` nominal-assignability special case enabled on\nPython 3.13+ to avoid spurious `None` inference through `close() -\u003e\nReturnT | None`.\n\nCloses https://github.com/astral-sh/ty/issues/3583\n\n## Testing\nAdded a regression mdtest."
    },
    {
      "commit": "099c89fe76117fdee24e910c286dbc4b56301ffa",
      "tree": "f2504ba2f7d0153889c248ad56f26b40be030a5e",
      "parents": [
        "7ef96bec9a1e8029e34ac3047c3ac13281f36911"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 16:53:11 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 14:53:11 2026 +0000"
      },
      "message": "[ty] Compact retained semantic maps (#25238)\n\n## Summary\n\nThis is a follow-up to #25102 to apply the same principles to a few more\ncollections. The benchmarks look good (-1% on some micro-benchmarks, +1%\non some real projects) and there are moderate memory savings."
    },
    {
      "commit": "7ef96bec9a1e8029e34ac3047c3ac13281f36911",
      "tree": "9732f33a7246d34b0dd2a7617a2ad229673020c6",
      "parents": [
        "0f9b81063c8cd6c80e89c02917516be89362e48b"
      ],
      "author": {
        "name": "JohnKagunda",
        "email": "125447154+RafaelJohn9@users.noreply.github.com",
        "time": "Mon Jun 01 17:36:36 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 14:36:36 2026 +0000"
      },
      "message": "[`pydocstyle`] Improve discoverability of rules enabled for each convention (#24973)\n\n## Summary\n\nImproves discoverability of `pydocstyle` convention-based rule filtering\nby moving the authoritative documentation from the FAQ into the\n`lint.pydocstyle.convention` settings reference, per #15217.\nCloses #15217.\n\n## Test Plan\n\nDocumentation-only change no functional code modifications.\n\nVerification steps:\n1. Ran `cargo dev generate-docs` to regenerate `docs/settings.md` and\n`ruff.schema.json` from updated docstrings\n2. Ran `cargo fmt --all` to ensure Rust formatting compliance\n3. Ran `cargo clippy --workspace --all-targets --all-features -- -D\nwarnings` - no new warnings\n4. Served docs locally with `uvx --with-requirements\ndocs/requirements.txt -- mkdocs serve -f mkdocs.yml` and verified:\n- The `lint.pydocstyle.convention` section renders with the new content\n   - The FAQ back-reference links to the settings section as intended\n   \n   ## Screenshots\n   \n   ### `/ruff/settings/#lint.pydocstyle`\n\n\u003cimg width\u003d\"1920\" height\u003d\"957\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/d35e94a0-0968-43fb-bfe5-dd2821655716\"\n/\u003e\n\n### `/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings`\n\n\u003cimg width\u003d\"1920\" height\u003d\"952\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/b8904757-f7c9-43c7-ac20-e0fbdb713acd\"\n/\u003e\n\n---------\n\nSigned-off-by: RafaelJohn9 \u003crafaeljohb@gmail.com\u003e\nCo-authored-by: Brent Westbrook \u003c36778786+ntBre@users.noreply.github.com\u003e"
    },
    {
      "commit": "0f9b81063c8cd6c80e89c02917516be89362e48b",
      "tree": "cad1a0cf2fb0e21a1b0cb78458b831e6770c7489",
      "parents": [
        "5ce05c733252a78455d0079206927a7bc8f18159"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 16:34:44 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 16:34:44 2026 +0200"
      },
      "message": "[ty] Deduplicate retained use-def place states (#25450)\n\n## Summary\n\nWhen we finish building a semantic index, we retain the end-of-scope and\nreachable bindings and declarations for every symbol and member. We\nalready interned some of these values, but retained symbol states and\nreachable states were still stored inline. Many of those states repeat,\nespecially for undefined places. This PR stores retained use-def states\nas compact IDs into the existing bindings and declarations arenas.\n\nI think the tradeoffs here are favorable: a 2.85% memory reduction on\nPrefect, a +1% walltime change on some real projects, a -1% a few\nothers, and up to -3% on some of the micro-benchmarks. But it\u0027s a big\nmemory win."
    },
    {
      "commit": "5ce05c733252a78455d0079206927a7bc8f18159",
      "tree": "58341a3e4629671e98bc4ff9b445429ad94bc3bf",
      "parents": [
        "a3c71eb741d4be0225bc8fc08ee3fbcae1289772"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Mon Jun 01 14:58:49 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 14:58:49 2026 +0100"
      },
      "message": "[ty] reduce features of low-level crates depended on by `ty_python_semantic` (#25524)\n\n## Summary\n\nAccording to Codex, this gives us a small improvement in compile times"
    },
    {
      "commit": "a3c71eb741d4be0225bc8fc08ee3fbcae1289772",
      "tree": "e46dd4a80d5334005bf01f80a96ff56319d9d49c",
      "parents": [
        "e151e96d867e0a6567472b6283f502979d3b195b"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon Jun 01 14:25:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 14:25:51 2026 +0200"
      },
      "message": "[ty] Fix narrowing enum literal unions by member identity (#25520)\n\n## Summary\n\nPrior to this change, we failed to narrow a union containing an\nexplicitly annotated enum literal (e.g., `x: list[int] |\nLiteral[Answer.NO]`) when comparing it against the corresponding enum\nmember:\n\n```python\nfrom enum import Enum\nfrom typing import Literal\n\nclass Answer(Enum):\n    NO \u003d 0\n    YES \u003d 1\n\ndef f(x: list[int] | Literal[Answer.NO]):\n    if x is Answer.NO:\n        return\n    # Before: list[int] | Literal[Answer.NO]\n    # After: list[int]\n    reveal_type(x)\n```\n\nWe were comparing the outer literal types, so the intersection builder\nfailed to recognize that both types represented the\nsame enum member.\n\nNow, we compare the underlying enum-literal identities instead,\nrestoring narrowing for `is`, `is not`, `\u003d\u003d`, and `!\u003d`.\n\nCloses https://github.com/astral-sh/ty/issues/3606."
    },
    {
      "commit": "e151e96d867e0a6567472b6283f502979d3b195b",
      "tree": "5e46fb18735b795f3adecc35e2aaabbb799b73b4",
      "parents": [
        "8579296926a3dcf567fd488865d29e6ae43c8fc9"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Mon Jun 01 05:01:44 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 12:01:44 2026 +0000"
      },
      "message": "[ty] Test tagged union narrowing for named tuples (#25519)\n\n## Summary\n\nAdd an mdtest showing that tagged-union narrowing also works for\n`NamedTuple` classes."
    },
    {
      "commit": "8579296926a3dcf567fd488865d29e6ae43c8fc9",
      "tree": "2316034e82c2d4a69f6768fe7f439c2a8efee5e7",
      "parents": [
        "cc5a78aadafa77a9d5b711ac57b2b3138c729c5b"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Mon Jun 01 12:47:32 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 11:47:32 2026 +0000"
      },
      "message": "[ty] Disallow file-system access in `ty_python_core` (#25518)\n\n## Summary\n\nAll other ty crates have this in their `lib.rs` to enforce that\nfilesystem access goes via the `System` abstraction (ensuring that it is\ntracked by Salsa). This was missed in\nhttps://github.com/astral-sh/ruff/commit/461994073e2f6cac13a28e60b06038ba50214ffc."
    },
    {
      "commit": "cc5a78aadafa77a9d5b711ac57b2b3138c729c5b",
      "tree": "f3f0b9006526de5aec132e67a92a023e485a0f10",
      "parents": [
        "a6ad57f5c27119dca5b7ce2e68c5139bb93d5dd9"
      ],
      "author": {
        "name": "Alex Holyoke",
        "email": "alexholyoke@gmail.com",
        "time": "Mon Jun 01 07:44:59 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 04:44:59 2026 -0700"
      },
      "message": "[ty] Nominal Tagged Union Narrowing (#24916)\n\n## Summary\nAdds narrowing for tagged unions of nominal class instances based on\nliteral attribute comparisons. Addresses part of astral-sh/ty#1479.\nRelated to astral-sh/ty#2897.\n\nThis allows ty to narrow code such as `x.tag \u003d\u003d \"a\"`, `\"a\" \u003d\u003d x.tag`,\n`x.tag !\u003d \"a\"`, and `match x.tag` when `x` is a union of classes whose\ntag attributes are annotated with supported literal types.\n* non-literal tag arms are preserved during positive narrowing\n* impossible literal arms are removed\n* unsupported cases such as enum literals are left for future work\n\nTo keep the performance impact small, the new place tracking is gated by\ncomparison operator. We only add base places for attribute/subscript\nexpressions when the operator can produce a relevant narrowing\nconstraint, which avoids extra Salsa narrowing queries for unrelated\ncomparisons.\n\n\n## Test Plan\n\n* Updated md tests\n* Ran ecosystem analyzer on pydantic and prefect\n* Ran memory profiler\n\nCo-authored-by: David Peter \u003cmail@david-peter.de\u003e"
    },
    {
      "commit": "a6ad57f5c27119dca5b7ce2e68c5139bb93d5dd9",
      "tree": "a7b0778f9bc0cd9338b742585f3513b0107f2449",
      "parents": [
        "88d851449d2345e11da991794f3e9bfbbd666719"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Mon Jun 01 11:54:51 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 11:54:51 2026 +0100"
      },
      "message": "Commit `scripts/uv.lock` (#25517)\n\n## Summary\n\nSome of the scripts in this directory are standalone PEP-723 scripts,\nbut other scripts in this directory depend on the dependencies in\n`scripts/pyproject.toml` being installed. If you run any of these\nscripts with `uv run`, uv creates a `scripts/uv.lock` file, but we\nhaven\u0027t committed it up till now. We probably should, for\nreproducibility and security apart from anything else. It also makes\nlife less confusing for agents, which are often perturbed by generated\nfiles unexpectedly popping up locally."
    },
    {
      "commit": "88d851449d2345e11da991794f3e9bfbbd666719",
      "tree": "02be5656a3c69c093adf9b1f50dcf74e09398f7b",
      "parents": [
        "a8d0fffc4d72da193b2a2ae4c53277fdf1f1794d"
      ],
      "author": {
        "name": "Minh Vu",
        "email": "vuhoangminh97@gmail.com",
        "time": "Mon Jun 01 09:01:49 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 08:01:49 2026 +0100"
      },
      "message": "Fix potential index out of range in `LineIndex` computation (#25492)\n\nCo-authored-by: Micha Reiser \u003cmicha@reiser.io\u003e"
    },
    {
      "commit": "a8d0fffc4d72da193b2a2ae4c53277fdf1f1794d",
      "tree": "f64c68149967b6e05a725c7071ef292b7152ebc1",
      "parents": [
        "dba3619fb385289cd43705609708efc0230ea6a6"
      ],
      "author": {
        "name": "github-actions[bot]",
        "email": "41898282+github-actions[bot]@users.noreply.github.com",
        "time": "Mon Jun 01 08:00:51 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 08:00:51 2026 +0100"
      },
      "message": "[ty] Sync vendored typeshed stubs (#25514)\n\nClose and reopen this PR to trigger CI\n\n---------\n\nCo-authored-by: typeshedbot \u003c\u003e"
    },
    {
      "commit": "dba3619fb385289cd43705609708efc0230ea6a6",
      "tree": "f207eeaf94f437ddd02b2d71de3f18a545c54a41",
      "parents": [
        "6d2ca1a4c3466a27095439e5899dc7e7cd894090"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat May 30 17:25:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 17:25:45 2026 +0200"
      },
      "message": "[ty] Add disjointness for protocol method members (#25315)\n\n## Summary\n\nThis PR implements disjointness checking for protocols with method\nmembers by comparing non-`Never` return types, e.g., we now understand\nthat a fixed-length tuple whose synthesized `__len__` returns\n`Literal[3]` is disjoint from a protocol requiring `__len__` to return\n`Literal[2]`:\n\n```python\nfrom typing import Literal, Protocol\nfrom ty_extensions import is_disjoint_from, static_assert\n\nclass HasLengthTwo(Protocol):\n    def __len__(self) -\u003e Literal[2]: ...\n\nstatic_assert(is_disjoint_from(tuple[int, int, int], HasLengthTwo))\n```\n\nThis is a prerequisite for using synthesized exact-length protocols to\nnarrow unions of fixed-length tuples and sequence patterns (see:\nhttps://github.com/astral-sh/ruff/pull/22134#issuecomment-4517932605).\n\nPer that comment, this isn\u0027t fully sound: a subclass can override an\notherwise incompatible method with a `Never`-returning implementation\nand therefore inhabit both types that we infer to be disjoint."
    },
    {
      "commit": "6d2ca1a4c3466a27095439e5899dc7e7cd894090",
      "tree": "d2a496e832149afd72cec406d94c999d65b58b21",
      "parents": [
        "f4923acb7cb015d26a5512c9139b8b890cb28211"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat May 30 15:47:56 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 15:47:56 2026 +0200"
      },
      "message": "[ty] Use compact sets for more immutable fields (#25476)"
    },
    {
      "commit": "f4923acb7cb015d26a5512c9139b8b890cb28211",
      "tree": "a470d12f3641b2efb999da0b570e695042cb7953",
      "parents": [
        "6c88390f7d49068c6fc2a0102145c3bdd5318e35"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Sat May 30 14:22:41 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 13:22:41 2026 +0000"
      },
      "message": "[ty] Derive `Default` for `FunctionDecoratorInference` (#25482)\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\n\n## Test Plan\n\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "6c88390f7d49068c6fc2a0102145c3bdd5318e35",
      "tree": "a11588e5c35c5b98498bd606e88cc1612e73fb35",
      "parents": [
        "d33907ff86b8888b11a8bfab7937aa95fe9a615a"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat May 30 11:04:59 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 11:04:59 2026 +0200"
      },
      "message": "[ty] Ignore rejected assignments for synthesized bindings (#25340)\n\n## Summary\n\nPrior to this change, we were retaining \"known key\" bindings from the\nright-hand side of an assignment even when the enclosing assignment was\nrejected. For example:\n\n```python\ndef _(x: dict[str, int]):\n    x \u003d {\"a\": \"bad\"}  # error: invalid-assignment\n    reveal_type(x[\"a\"])  # Should be int, but main shows Literal[\"bad\"]\n```\n\nSo we\u0027re respecting the new type information from `x \u003d {\"a\": \"bad\"}`...\nBut an invalid assignment shouldn\u0027t contribute new type information!\n\nIn other words, we want to ignore the known-key bindings that we get\nfrom the dict assignment when the overall assignment is invalid.\n\nDoing so is pretty ugly because the index is built solely from syntax\n(e.g., we create definitions from `x \u003d {\"a\": \"bad\"}`), but we\u0027re trying\nto _reject_ those definitions later during inference, using information\nwe don\u0027t have at the time of definition creation. I tried a variety of\napproaches but this ended up being the cheapest and least invasive thing\nI could find."
    },
    {
      "commit": "d33907ff86b8888b11a8bfab7937aa95fe9a615a",
      "tree": "7f646eb9e022803040d7309a1498d79dab88265b",
      "parents": [
        "042a6a172983036399530f758c6e74171bcd6216"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat May 30 10:51:21 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 10:51:21 2026 +0200"
      },
      "message": "[ty] Handle cycles in function decorator inference (#25475)\n\n## Summary\n\nHandle recursive `function_known_decorators` queries by falling back to\nan empty decorator inference result while Salsa resolves the cycle. This\nprevents a Python 3.14 descriptor annotation with an overloaded\n`__get__` method from panicking and preserves conservative `Unknown`\nbehavior during cycle recovery.\n\nCloses https://github.com/astral-sh/ty/issues/3593."
    },
    {
      "commit": "042a6a172983036399530f758c6e74171bcd6216",
      "tree": "519074ba71db61a47ce0b1580daf526ef325892c",
      "parents": [
        "a5cc4e79e33ecba98a1d4c837c1fd78e04efd87b"
      ],
      "author": {
        "name": "Tejas Amle",
        "email": "40148050+TejasAmle@users.noreply.github.com",
        "time": "Sat May 30 13:27:12 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 09:57:12 2026 +0200"
      },
      "message": "docs: fix typo `bin/active` → `bin/activate` in tutorial (#25473)\n\n## Summary\n\nFixes a one-character typo in  (line 61).\n\nThe Linux/macOS virtual environment activation command was written as:\n\n```\nsource .venv/bin/active\n```\n\nbut should be:\n\n```\nsource .venv/bin/activate\n```\n\nThe Windows command on the same line () was already correct, making the\ndiscrepancy easy to spot.\n\n## How verified\n\nThe fix was verified by reading the file directly — is not a valid shell\nscript; the correct file installed by Python’s `venv` module is always .\n\nCo-authored-by: Claude \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "a5cc4e79e33ecba98a1d4c837c1fd78e04efd87b",
      "tree": "b5ace715b6c4a3dfcba291ab4f4b957269a11a3c",
      "parents": [
        "5a6c811505e5068bc98752944830d244f68b9bb6"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Sat May 30 09:37:34 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 30 09:37:34 2026 +0200"
      },
      "message": "[ty] Narrow bound method overloads by receiver (#24707)\n\n## Summary\n\nGiven:\n\n```python\nfrom typing import overload\n\nclass Base:\n    @overload\n    def convert(self: \"Base\", x: int) -\u003e int: ...\n\n    @overload\n    def convert(self: \"Child\", x: str) -\u003e str: ...\n\n    def convert(self, x: int | str) -\u003e int | str:\n        return x\n\nclass Child(Base): ...\n```\n\nOn main, we treat `Base().convert` as if both overloads are still\npossible, including the overload that requires `self: \"Child\"`.\n\nAfter this PR:\n\n```python\nreveal_type(Base().convert)\n# bound method Base.convert(x: int) -\u003e int\n\nreveal_type(Child().convert)\n# Overload[(x: int) -\u003e int, (x: str) -\u003e str]\n```\n\nSo `Base().convert(\"x\")` is no longer considered valid via narrowing on\nthe actual receiver type.\n\nCloses https://github.com/astral-sh/ty/issues/2693.\n\nCloses https://github.com/astral-sh/ty/issues/2612.\n\nCloses https://github.com/astral-sh/ty/issues/3380.\n\nCloses https://github.com/astral-sh/ty/issues/1169."
    },
    {
      "commit": "5a6c811505e5068bc98752944830d244f68b9bb6",
      "tree": "a37ad3b7a6ffbbf184d09ad9dafab6a287c9ca54",
      "parents": [
        "a8aceb1a5a3f74c676ee4cf9579ef3c2cd6f9336"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Fri May 29 23:01:39 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 23:01:39 2026 +0200"
      },
      "message": "[ty] Avoid storing redundant reachability indexes (#25453)\n\n## Summary\n\nWhen all reachability nodes for a scope are retained, their IDs already\nmatch their positions in the stored node array. In that case, this PR\navoids storing an extra bit vector for mapping node IDs back to the\narray. We only allocate that mapping when unused intermediate nodes were\nremoved.\n\nThis reduces retained memory for reachability constraints while\npreserving the existing compaction for scopes that need it. It also\nmoves node lookup into `ReachabilityConstraints::get_interior_node`, so\nboth paths use the same lookup logic."
    },
    {
      "commit": "a8aceb1a5a3f74c676ee4cf9579ef3c2cd6f9336",
      "tree": "a84af2cb1f97100887722bf35e975976931daff2",
      "parents": [
        "0fb46b21c9048178db3af25366b7e7aeebce2d30"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Fri May 29 13:51:19 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 13:51:19 2026 -0700"
      },
      "message": "[ty] Improve diagnostic for failed assignment to a `Callable` type. (#25308)\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 improves the diagnostic we show when an assignment to a `Callable`\ntype fails. That diagnostic now contains additional context that\ndescribes the inferred callable type that failed. The result is that the\nfull explanation of the failed assignment is more coherent.\n\nCloses https://github.com/astral-sh/ty/issues/866.\n\n## Test Plan\n\nPlease see updated tests.\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "0fb46b21c9048178db3af25366b7e7aeebce2d30",
      "tree": "7fb0ceda25ea1bfcc31d2a547845f1fe5e30afaa",
      "parents": [
        "49882fa3844b9f93a7985dfd277a3348975bc576"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Fri May 29 20:16:58 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 20:16:58 2026 +0100"
      },
      "message": "Add missing primer projects to good.txt (#25461)\n\n## Summary\nAdd `django-modern-rest` and `pytest-autoprofile` to the ty primer\n`good.txt` list. These upstream projects completed `uvx ty check`\nwithout crashing, panicking, or timing out"
    },
    {
      "commit": "49882fa3844b9f93a7985dfd277a3348975bc576",
      "tree": "f29baeb6c2f6d091f6fe146310d492e7a4c8ff0e",
      "parents": [
        "f46ee089e7a1dcb5019c3a525afde416fe807159"
      ],
      "author": {
        "name": "Aditya Singh",
        "email": "60082699+adityasingh2400@users.noreply.github.com",
        "time": "Fri May 29 12:10:21 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 12:10:21 2026 -0700"
      },
      "message": "[`pyflakes`] Avoid removing the `format` call when it would change behavior (`F523`) (#25320)\n\n## Summary\n\nWhen every positional argument is unused, `F523` removes the entire\n`.format(...)` call. That changes behavior whenever the format string\nrelies on the call:\n\n* Brace escapes: `\"{{\".format(\"!\")` returns `\"{\"`, but `\"{{\"` is two\ncharacters.\n* Named placeholders: `\"{x}\".format(\"!\")` raises `KeyError`, but `\"{x}\"`\nquietly succeeds.\n\nThis detects these cases and avoids removing the entire `format` call.\nWe also tried the \"ideal\" fix described on the issue of processing the\nstring to handle escapes ourselves, but just preserving the call seemed\nlike the better tradeoff in terms of complexity.\n\n## Test plan\n\nNew mdtests based on the examples from the issue\n\nCloses #15557\n\n---------\n\nCo-authored-by: Brent Westbrook \u003cbrentrwestbrook@gmail.com\u003e"
    },
    {
      "commit": "f46ee089e7a1dcb5019c3a525afde416fe807159",
      "tree": "bab547804b0e6686995282a73f21e5816fa746e2",
      "parents": [
        "1518f1d5fdcc92d207c5018bac6d0b9110643240"
      ],
      "author": {
        "name": "Carl Meyer",
        "email": "carl@astral.sh",
        "time": "Fri May 29 11:59:47 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 11:59:47 2026 -0700"
      },
      "message": "[ty] agent guidance to avoid Python 3.7 pitfall (#25466)\n\nPer title; this is a trap Codex is constantly falling into."
    },
    {
      "commit": "1518f1d5fdcc92d207c5018bac6d0b9110643240",
      "tree": "913a98b9dafeef4671bdd15bc1987d587df35583",
      "parents": [
        "572e4b58e3d438f5e05b375702d65474b3dd040d"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri May 29 20:17:16 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 18:17:16 2026 +0000"
      },
      "message": "Drop excess capacity from Suites during parsing (#25368)\n\n## Summary\n\nShrink Suite vectors to drop the excess capacity during parsing. \n\nI excluded other nodes for now to get a better sense of the performance\nand memory impact.\n\nI\u0027m a bit conflicted on this. This is a pretty huge memory improvement\nfor ty, but there are a few linter benchmarks that regress by 1-2%\nbecause some vectors need to be copied to smaller allocations, which\nhurts performance. For the linter and formatter, it\u0027s also not important\nto shrink the vectors, because the AST is never stored for long. But\nthis is different for ty where we cache the AST.\n\nI\u0027m curious to hear what others think on this. I could also try to\nreduce the places where we call `shrink_to_fit`, e.g., only for\nstatements?"
    },
    {
      "commit": "572e4b58e3d438f5e05b375702d65474b3dd040d",
      "tree": "b032300e42a0c7bad7bc0cf4ec98a8950657d6fe",
      "parents": [
        "d475a239bb5c5f5ace5a2b3d995c2710fcfa08c8"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Fri May 29 19:44:44 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 19:44:44 2026 +0200"
      },
      "message": "[ty] Compact retained semantic arrays (#25454)\n\n## Summary\n\nReduces the memory retained by Salsa-cached semantic indexes by using a\n`FrozenIndexVec` for index-addressed immutable arrays, like the\n`FrozenMap` abstraction.\n\nApart from a \u003e1% memory reduction, we see a 1% improvement on `attrs`,\n`anyio`, `hydra-zen`, `multithreaded`; 1% regression on `pydantic` and\n`freqtrade`."
    },
    {
      "commit": "d475a239bb5c5f5ace5a2b3d995c2710fcfa08c8",
      "tree": "4d8470892292cfd3859d23d3180853f1d3f44a7a",
      "parents": [
        "ec1d5358abc115b1f89258c4b432e5013f46680a"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Fri May 29 18:33:18 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 18:33:18 2026 +0200"
      },
      "message": "[ty] Use ThinVec for sparse kwargs bindings (#25457)\n\n## Summary\n\n`multi_bindings_by_use` is only populated for kwargs expressions (like\n`f(**options)`), so it is empty or very small for most scopes. This PR\nkeeps the mutable hash map while building the use-def map, then converts\nit into a sorted `ThinVec` for storage, which is much smaller\n(especially when empty). Lookups use binary search by `ScopedUseId`."
    },
    {
      "commit": "ec1d5358abc115b1f89258c4b432e5013f46680a",
      "tree": "f3f3465f2777bd4d7ceecb0c267bd28fd571475b",
      "parents": [
        "35ee0888f4ed173ed35d2fcd31d261a085a89c21"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Fri May 29 16:58:20 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 15:58:20 2026 +0000"
      },
      "message": "make ecosystem-analyzer self-aware (#25460)\n\n## Summary\n\nUpdate to the latest upstream commit of ecosyste-analyzer following\nhttps://github.com/hauntsaninja/mypy_primer/pull/249 and\nhttps://github.com/astral-sh/ecosystem-analyzer/pull/72. This means that\necosystem-analyzer now checks ty by running ty on ecosystem-analyzer, a\nvery satifsying ouroboros\n\n## Test Plan\n\nci on this pr"
    },
    {
      "commit": "35ee0888f4ed173ed35d2fcd31d261a085a89c21",
      "tree": "a4b0fe499596ebc446daab7b921baccc5a86224f",
      "parents": [
        "1a8520ea168d281a8c2644dd36a04b9a8e5966db"
      ],
      "author": {
        "name": "Anish Giri",
        "email": "161533316+anishgirianish@users.noreply.github.com",
        "time": "Fri May 29 09:10:37 2026 -0500"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 07:10:37 2026 -0700"
      },
      "message": "[`ruff`] Treat `yield` before `break` from a terminal loop as terminal (`RUF075`) (#25447)\n\n## Summary\n\nCloses #25378.\n\nExtends the yield-before-return terminal check to also cover\nyield-before-break, but only when the enclosing loop is itself in a\nterminal position. break exits the innermost loop, so a yield right\nbefore it has no cleanup code after it in that case.\n\n## Test Plan\n\nmdtest passes for fallible-context-manager.md across the new and\nexisting cases"
    },
    {
      "commit": "1a8520ea168d281a8c2644dd36a04b9a8e5966db",
      "tree": "076a14ca4d1a498915f9731a969877ea29ce60d4",
      "parents": [
        "0c1a69c011c12723717e39bba39f64957d0733cf"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Fri May 29 07:06:09 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 07:06:09 2026 -0700"
      },
      "message": "[ty] Use `TypeForm` in `ty_extensions` (#25421)\n\n## Summary\n\nI saw that TypeForm support landed, and I remembered @dcreager saying\nthat we could simplify some things once we had that.\n\n## Test Plan\n\nUpdated tests, this is mostly intended to be an internal refactor."
    },
    {
      "commit": "0c1a69c011c12723717e39bba39f64957d0733cf",
      "tree": "ab4e98e4548a2ad1793bce9db90da23121016eba",
      "parents": [
        "a963b83c5edca89db571b97ea76a5893856a28c0"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Fri May 29 15:24:56 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 14:24:56 2026 +0100"
      },
      "message": "[ty] Add function parentheses completion (#25305)\n\n## Summary\n\nAdd an opt-in completion setting that inserts parentheses snippets for\ncallable completions.\n\nCloses https://github.com/astral-sh/ty/issues/977\n\n## Test Plan\n\n\nhttps://github.com/user-attachments/assets/4077d726-b0e3-4ed8-ab4a-63512b01298e"
    },
    {
      "commit": "a963b83c5edca89db571b97ea76a5893856a28c0",
      "tree": "6ead227e4881c15c2b7bc282c17a68a956e1ac2a",
      "parents": [
        "6627489bdac3033bb191c1653deee31364061607"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Fri May 29 13:38:23 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 12:38:23 2026 +0000"
      },
      "message": "More updates to ecosystem summary skills (#25458)\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\n\n## Test Plan\n\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "6627489bdac3033bb191c1653deee31364061607",
      "tree": "1c27fecaf6ac1e5b20ec7ded548bd6b394a62a74",
      "parents": [
        "47d742f520dc6352209710dcad9b40d55c0f3b44"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Fri May 29 12:27:01 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 11:27:01 2026 +0000"
      },
      "message": "Further improve instructions for agents for working on ty (#25430)\n\n## Summary\n\nA bunch of followup changes from\nhttps://github.com/astral-sh/ruff/pull/25422. Mostly triggered by using\nthe skill for the first time to analyze the report in\nhttps://github.com/astral-sh/ruff/pull/24761.\n\n- Move ty-specific guidance from `AGENTS.md` to a new local\n`working-on-ty` skill. Link to the other ty-specific skills from that\nskill.\n- Add more keyword triggers to various skills so that agents are more\nlikely to automatically figure out that a skill is appropriate\n(suggested by @dhruvmanila).\n- Add more guidance to the ecosystem-minimization skill (and tweak the\nscript for setting up a primer project) so that agents are likely to\nreproduce results in a deterministic and helpful way.\n\n## Test Plan\n\nI repeatedly ran `/review` with Codex on this until the only remaining\nissues were trivial."
    },
    {
      "commit": "47d742f520dc6352209710dcad9b40d55c0f3b44",
      "tree": "e0a9b4ccbcfe003c3f538ab913ae309e93e7aebc",
      "parents": [
        "ae44d57db5ffcf010fd2c685a4c0b75564d62381"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Fri May 29 09:29:56 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 08:29:56 2026 +0100"
      },
      "message": "[ty] Infer `bool` for `not` applied to dynamic values (#25445)\n\n## Summary\n\nPrior to this change, we treated all unary operations applied to a\ndynamic value as producing the same dynamic type. This is appropriate\nfor `+`, `-`, and `~`, whose dunder implementations may return arbitrary\nvalues, but not for `not`: Python always produces a `bool` after\nevaluating its operand\u0027s truthiness.\n\n```python\nfrom typing import Any, reveal_type\n\ndef check(value: Any) -\u003e None:\n    result \u003d reveal_type(not value)  # bool\n    result.nonexistent()  # error: `bool` has no attribute `nonexistent`\n```\n\nWe now allow `not` on dynamic operands to use the existing\ntruthiness-inference path, while retaining dynamic propagation for the\nother unary operators. This restores downstream checking of the\nresulting boolean value.\n\nCloses https://github.com/astral-sh/ty/issues/3572."
    },
    {
      "commit": "ae44d57db5ffcf010fd2c685a4c0b75564d62381",
      "tree": "1d701d93f8a184d7db987891edd0da2ced8bbad7",
      "parents": [
        "f6bfc7f8cc889caef07f4cd6833e5e4fc262d041"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Fri May 29 09:20:25 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 08:20:25 2026 +0100"
      },
      "message": "[ty] Avoid panic for deferred dataclass field annotations (#25444)\n\n## Summary\n\nI fixed https://github.com/astral-sh/ty/issues/3493 in\nhttps://github.com/astral-sh/ruff/pull/25249, but missed the case of\ndeferred evaluation for annotations... For deferred evaluation, when we\nthen checked whether that field was a descriptor,\n`class_member_with_policy` attempted meta-type lookup on the cycle\nmarker itself and panicked.\n\nE.g., on Python 3.14, where annotations are deferred by default:\n\n```python\nfrom dataclasses import InitVar, dataclass\nfrom ty_extensions import Top\n\n@dataclass\nclass C:\n    a: Top[int]\n    int: InitVar[int] \u003d 0\n\nC()\n```\n\nWe now apply the materialized divergent fallback before class-member\nlookup, consistent with the other lookup and descriptor operations. (The\nexample reports the expected missing argument for `a` instead of\npanicking, and is covered under Python 3.14.)\n\nCloses https://github.com/astral-sh/ty/issues/3493."
    },
    {
      "commit": "f6bfc7f8cc889caef07f4cd6833e5e4fc262d041",
      "tree": "f1158a41861d1e329db4d7cd88e60e130e8339dc",
      "parents": [
        "f266e8f99d6e9f27380de3963b3d5c01f87a1150"
      ],
      "author": {
        "name": "Dhruv Manilawala",
        "email": "dhruvmanila@gmail.com",
        "time": "Fri May 29 12:45:39 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 29 08:15:39 2026 +0100"
      },
      "message": "Update Rust toolchain to 1.96 and MSRV to 1.94 (#25443)\n\n## Summary\n\n- Update the pinned Rust toolchain from 1.95 to 1.96.\n- Raise Ruff\u0027s minimum supported Rust version from 1.93 to 1.94,\nfollowing the N-2 policy.\n- Replace the now-unnecessary `zip_opt` helper with `Option::zip`, as\nRust 1.96\u0027s Clippy flags the manual implementation.\n\n## Test plan\n\n`cargo clippy --workspace --all-targets --all-features -- -D warnings`"
    },
    {
      "commit": "f266e8f99d6e9f27380de3963b3d5c01f87a1150",
      "tree": "056eb7045e9bb13be543f08a76e75bec2f47ff3c",
      "parents": [
        "2110d320da20d1f6fc20b0397f75b0d08a13d5f8"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Thu May 28 22:47:56 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 22:47:56 2026 -0700"
      },
      "message": "[ty] Introduce opt-in `missing-override-decorator` rule. (#25111)\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\n\nThis adds a new rule called `missing-override-decorator` that allows\nusers to opt into `@override` enforcement. The rule is ignored by\ndefault.\n\nWhen the rule is enabled, ty will report a missing override decorator on\nan override of any method other than `__init__`, `__new__`,\n`__init_subclass__` or `__post_init__`. This matches the general\nstrategy taken by other type checkers (see discussion starting\n[here](https://github.com/astral-sh/ruff/pull/25111#issuecomment-4501847451)).\n\nCloses https://github.com/astral-sh/ty/issues/155.\n\n## Test Plan\n\nPlease see included tests.\n\nNote that my approach to testing this feature departs from precedent in\nthat, unlike other rules, `missing-override-decorator` is not enabled by\ndefault in our test suite. I think we can and should change this in\nfollow-ups, but for the moment that choice makes this PR easier to\nreview by eliminating churn in unrelated tests."
    },
    {
      "commit": "2110d320da20d1f6fc20b0397f75b0d08a13d5f8",
      "tree": "d265e37d54bc58dee446c2e27a55a8447e5f3cbd",
      "parents": [
        "f9ba228f6c99f6364d2936ed62a0278e6856bfed"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu May 28 17:24:12 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 14:24:12 2026 -0700"
      },
      "message": "[`pylint`] Narrow diagnostic range and exclude cases without exception handlers (`PLW0717`) (#25440)\n\nSummary\n--\n\nThis PR fixes #25438 by narrowing the diagnostic range from the entire\n`try` statement to only the\n`try` keyword. This was one of the two alternatives mentioned on #25438,\nand I thought this looked a\nbit nicer than only narrowing the range to the `try` body. I think this\nis also consistent with the\nrange for `too-many-statements` (`PLR0915`), which marks the function\nname:\n\nhttps://play.ruff.rs/dafe23a1-2e89-48a5-b4d7-b0825b737152\n\nI\u0027m happy to reconsider, though. Another possible alternative along\nthese lines is marking the first\nstatement that exceeds the limit, or the last statement, or something\nlike that.\n\nThis PR also fixes #25390 by limiting the rule to `try` statements with\n`except` handlers. Either an\n`except` or a `finally` clause is required, so this avoids emitting\ndiagnostics for\ncontext-manager-like cases, such as the one reported in the issue:\n\n```py\nsession \u003d ...\ntry:\n    print()\n    print()\n    print()\n    print()\n    print()\n    print()\nfinally:\n    session.close()\n```\n\nwhere the `try` is just ensuring that some cleanup is performed and\ncan\u0027t actually trigger the bad\nbehavior described in the rule docs of mistakenly catching the wrong\nexception.\n\nTest Plan\n--\n\nNew mdtests based on the issues"
    },
    {
      "commit": "f9ba228f6c99f6364d2936ed62a0278e6856bfed",
      "tree": "2d4bfe1e6b6d72d90a10635cb90a896681687b73",
      "parents": [
        "ba58c8409aa562bc5a8b97d4165031658f96a259"
      ],
      "author": {
        "name": "Mirco Huennefeld",
        "email": "mirco.huennefeld@tu-dortmund.de",
        "time": "Thu May 28 11:57:47 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 18:57:47 2026 +0000"
      },
      "message": "[ty] Add call hierarchy support (#25338)\n\nCo-authored-by: Micha Reiser \u003cmicha@reiser.io\u003e"
    },
    {
      "commit": "ba58c8409aa562bc5a8b97d4165031658f96a259",
      "tree": "701f3dba1187f18ac54fb98ed9dc30a6c9b7c208",
      "parents": [
        "320a4827094acac27378d2a42d4a4c3a34d62867"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu May 28 14:54:52 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 18:54:52 2026 +0000"
      },
      "message": "[`ruff`] Restore example code for Python versions before 3.15 (`RUF017`) (#25439)\n\nSummary\n--\n\nCloses #25436 by restoring the previous `Use instead:` snippet from\nbefore #23789\n\nI thought it would be cool to use the tabs like we have for\npyproject.toml vs ruff.toml snippets, but I wasn\u0027t sure if that would\nrender well in other contexts where we use rule docs and opted just to\nhave two separate code blocks."
    },
    {
      "commit": "320a4827094acac27378d2a42d4a4c3a34d62867",
      "tree": "44da9d620a737c2bca552fb1754101867c6bf47e",
      "parents": [
        "e70d974f66edc7ddce176f1ce9f46d63d82d318d"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu May 28 14:43:49 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 11:43:49 2026 -0700"
      },
      "message": "Update snapshot source lines for mdtests (#25437)\n\nSummary\n--\n\nThis follows up on the mdtest refactor to force-update the snapshots\nthat now report different\n`source`s as noted in\nhttps://github.com/astral-sh/ruff/pull/24954#discussion_r3319740141.\n\nTest Plan\n--\n\n```shell\ncargo insta test --force-update-snapshots -p ty_python_semantic --test mdtest\n```\n\nI also followed up with the more general:\n\n```shell\ncargo insta test --force-update-snapshots\n```\n\nwhich didn\u0027t reveal any other (related) issues."
    },
    {
      "commit": "e70d974f66edc7ddce176f1ce9f46d63d82d318d",
      "tree": "33ab745877099fa9cb473dfb33e3bc89d682289c",
      "parents": [
        "b427926f2014abf7204cc433d6cb5204c8bbc49c"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Thu May 28 16:54:33 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 15:54:33 2026 +0100"
      },
      "message": "[ty] Avoid treating metaclass declarations as populated values (#25432)\n\n## Summary\n\nPrior to this change, we conflated a metaclass instance-member\ndeclaration with a value populated on each newly created class object.\n\nSo in the example below, we correctly validate the stored value\n`staticmethod(identity)` against the metaclass contract `Callable[[str],\nstr]`, because `C` is an instance of `Meta`. But we then _incorrectly_\nperformed the reverse validation: we treated `Meta.factory`\u0027s declared\n`Callable[[str], str]` type as though `Meta` had written a replacement\nvalue into `C.factory`, and rejected it against `C.factory`\u0027s declared\nstorage type, `staticmethod[[str], str]`.\n\n```python\nfrom collections.abc import Callable\nfrom typing import ClassVar\n\nclass Meta(type):\n    factory: Callable[[str], str]\n\ndef identity(value: str) -\u003e str:\n    return value\n\nclass C(metaclass\u003dMeta):\n    factory: ClassVar[\"staticmethod[[str], str]\"] \u003d staticmethod(identity)\n```\n\nWe now distinguish declarations from assignments made to metaclass\ninstance members in methods: bare declarations, including `cls.factory:\nCallable[[str], str]`, continue to constrain values supplied by the\nclass body, but no longer act as writes that overwrite the constructed\nclass namespace.\n\nCloses https://github.com/astral-sh/ty/issues/3569."
    },
    {
      "commit": "b427926f2014abf7204cc433d6cb5204c8bbc49c",
      "tree": "815860e0774d02862d214efc2ffcd6c8fce1c310",
      "parents": [
        "db5aa0a5f1b92cb91d910bf0866a967554dd94f5"
      ],
      "author": {
        "name": "Venkata Srivaibhav Nukaraju",
        "email": "66205879+Redslayer112@users.noreply.github.com",
        "time": "Thu May 28 20:04:06 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 07:34:06 2026 -0700"
      },
      "message": "[`eradicate`] Fix `ERA001`/`RUF100` conflict when `noqa` is on commented-out code (#25414)\n\n## Summary\n\nFixes #25386.\n\nFor commented-out code with a trailing `# noqa: ERA001`, Ruff could\nreport both\n`ERA001` on one line and `RUF100` (unused `ERA001`) on the next — which\nis inconsistent.\n\n`ERA001` uses `comment_contains_code()` to decide if a comment is code.\nThat function\nwas analyzing the full comment body, including trailing `# noqa:\nERA001`. The allowlist\ntreats `noqa` as non-code, so lines like `# ) # noqa: ERA001` were\nskipped by `ERA001`\nwhile a nearby line without `noqa` still triggered `ERA001`. That made\nthe `noqa`\nlook unused to `RUF100`.\n\nThe fix strips a trailing inline `# noqa...` before ERA001’s code\ndetection, so the\ncomment is evaluated on its code portion only (e.g. `)`). Suppression\nand detection\nthen agree: `noqa: ERA001` is used when it suppresses a real `ERA001`\nmatch.\n\nthe repro now shows ERA001 diagnostics without incorrectly flagging the\npaired noqa: ERA001 as unused.\n\n## Test Plan\n\n- Added unit tests in `detection.rs` for `# ) # noqa: ERA001` and `# \\t(\n# noqa: ERA001`\n- Extended `RUF100_5.py` with regression cases from #25386\n- Ran `cargo test -p ruff_linter ruf100_5` (snapshot updated)\n- Ran `cargo test -p ruff_linter comment_contains_code_with_multiline`\n- Ran `uvx prek run --files` on the three changed files"
    },
    {
      "commit": "db5aa0a5f1b92cb91d910bf0866a967554dd94f5",
      "tree": "2558decee4a6bb76e699ed388b6759b9be3d0889",
      "parents": [
        "366fe21ba369ccdd01eb99c1043c9a969c99230b"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Thu May 28 09:54:03 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 06:54:03 2026 -0700"
      },
      "message": "Bump 0.15.15 (#25431)"
    },
    {
      "commit": "366fe21ba369ccdd01eb99c1043c9a969c99230b",
      "tree": "da8342c11577331e1d6463e630daa84ea971a08c",
      "parents": [
        "e2e1e647d182b8567845039c9a65fb0608a4dcfc"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Thu May 28 11:51:20 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 10:51:20 2026 +0000"
      },
      "message": "[ty] Improve diagnostics for syntax errors in forward annotations (#25158)\n\n## Summary\n\nFixes https://github.com/astral-sh/ty/issues/1627.\n\nHere\u0027s an example diagnostic with the current release of ty:\n\n\u003cimg width\u003d\"2286\" height\u003d\"286\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/46ab9154-aaf0-4451-b301-5ff12fcd8507\"\n/\u003e\n\nOn this branch, this diagnostic becomes:\n\n\u003cimg width\u003d\"1464\" height\u003d\"408\" alt\u003d\"image\"\nsrc\u003d\"https://github.com/user-attachments/assets/449e0b6c-58d2-4501-91df-c267db6f48ca\"\n/\u003e\n\nThe exact span of the node that creates the syntax error is now retained\nand highlighted in the diagnostic.\n\n## Implementation\n\nPropagating the range of the node inside the string annotation into the\ndiagnostic is trivial. However, naively implementing that quickly\nrevealed that this would make diagnostics like this unsuppressable:\n\n```py\nx: \"\"\"list[\n    yield from range(42)\n]\"\"\"\n```\n\nThe primary range of the diagnostic is now specifically the `yield from\nrange(42)` part of the string rather than the string node as a whole.\nBut I cannot add a `ty: ignore` comment that is either on or above the\n`yield from range(42)` part of the string -- the \"comment\" there would\njust become part of the string.\n\nThis PR also improves the consistency of our parser error messages in\ngeneral when it comes to capitalization.\n\n## Test Plan\n\nMdtests extended and updated\n\n---------\n\nCo-authored-by: Micha Reiser \u003cmicha@reiser.io\u003e"
    },
    {
      "commit": "e2e1e647d182b8567845039c9a65fb0608a4dcfc",
      "tree": "b3065fc3c829fb8cc5ce4b431637f95946fd1e0a",
      "parents": [
        "1bd77e1646f2213d86b8da215f08279187867d72"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Thu May 28 09:05:25 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 07:05:25 2026 +0000"
      },
      "message": "[ty] Remove excess capacity from more Salsa cached collections (#25411)"
    },
    {
      "commit": "1bd77e1646f2213d86b8da215f08279187867d72",
      "tree": "984df7699118d402e40ebe79c78cc48c8383a979",
      "parents": [
        "7e1bc1e75f15795f12c846294b13df4535f2abbf"
      ],
      "author": {
        "name": "Jelle Zijlstra",
        "email": "jelle@openai.com",
        "time": "Wed May 27 23:07:25 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 28 07:07:25 2026 +0100"
      },
      "message": "[ty] Use diagnostic message as tie breaker when sorting (#25424)"
    },
    {
      "commit": "7e1bc1e75f15795f12c846294b13df4535f2abbf",
      "tree": "efca0ebac6ef0412a66e4050add84d1e6e794dd6",
      "parents": [
        "574e10752f8cfa9e0cdbe3b01e96c4380950469b"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Wed May 27 22:02:29 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 22:02:29 2026 +0100"
      },
      "message": "Add agent skills for working on ty (#25422)\n\n## Summary\n\nThis PR adds a `.agents/skills` directory to the repo, and adds three\nskills to help agents work on ty more effectively:\n- A skill to help them write better ty diagnostics\n- A skill to help them minimize a single ecosystem change more\neffectively\n- A skill to help them analyze and summarise ecosystem reports more\neffectively\n\nThe skills have been designed so that they will (hopefully) work well\nwhether a contributor is using Codex or Claude as their agent locally.\nCodex should discover these skills automatically if you\u0027re working on\nthe repo; if you use Claude locally, you _should_ hopefully be prompted\nby Claude to install the local skills due to them being listed as\nrecommended skills in `.claude/settings.json`.\n\n## Test Plan\n\nI ran `/review` locally with Codex, instructing it to be as nitpicky as\npossible, until it could find no more issues with these skills."
    },
    {
      "commit": "574e10752f8cfa9e0cdbe3b01e96c4380950469b",
      "tree": "9071938fa5d5449bd788386a326243c8588ab526",
      "parents": [
        "4a7ca062fccd80443a43aa61e5dc7e5858e88dc1"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed May 27 22:39:41 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 21:39:41 2026 +0100"
      },
      "message": "Expand semantic syntax errors for invalid walruses (#25415)\n\n## Summary\n\nPython rejects assignment expressions in several comprehension contexts,\nbut we previously only reported the case in which a walrus rebound a\ncomprehension variable.\n\nAs a result, we accepted invalid walruses in the iterable and class-body\nportions clause of a comprehension, and missed some rebinding cases in\nfilter clauses and nested comprehensions. It turns out the rules are\nreally complicated!\n\nFor example, we now report the two newly modeled syntax errors:\n\n```py\n# error: [invalid-syntax] \"assignment expression cannot be used in a comprehension iterable expression\"\n[x for x in (values :\u003d [1])]\n\nclass C:\n    # error: [invalid-syntax] \"assignment expression within a comprehension cannot be used in a class body\"\n    [(value :\u003d item) for item in [1]]\n```\n\nWe also catch previously missed instances of the existing rebinding\nerror:\n\n```py\n# error: [invalid-syntax] \"assignment expression cannot rebind comprehension variable\"\n[x for x in [1] if (x :\u003d 0)]\n```\n\nIn general, I decided not to worry about emitting multiple diagnostics\nfor a single error. These are rare, they\u0027re fatal, and it added a lot of\ncomplexity (so in some cases we may emit two errors for a walrus that is\ninvalid for two reasons)."
    },
    {
      "commit": "4a7ca062fccd80443a43aa61e5dc7e5858e88dc1",
      "tree": "c8e9ec8e50b0f446d002b6f3690754a148f913f5",
      "parents": [
        "54327092dbfe455040690d63bb1e5e4b5f551239"
      ],
      "author": {
        "name": "Lérè",
        "email": "contact@lrbr.dev",
        "time": "Wed May 27 11:59:49 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 11:59:49 2026 -0700"
      },
      "message": "[ty] Display docs for matching parameter when hovering over the name of an argument passed by keyword. (#25283)\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\n\nThis makes it so that hovering over the name of an argument passed by\nkeyword shows the matching parameter label and documentation rather than\nthe inferred type of the argument value:\n\n```py\ndef test(ab: int):\n    \"\"\"my cool test\n\n    Args:\n        ab: a nice little integer\n    \"\"\"\n    return 0\n\ntest(ab\u003d123)\n      ^\n```\n\n````txt\n(parameter) ab: int\n---------------------------------------------\na nice little integer\n````\n\nTwo additional notes on this new behaviour:\n- For a parameter without any documentation, we will show just the\nparameter label: `(parameter) ab: int`.\n- We will also show the documentation for a variadic keyword parameter\n(i.e., `**kwargs`) when the argument name maps to that type of\nparameter.\n\nBy contrast, nothing changes when hovering over the argument value: it\nwill continue to show the inferred type of the value:\n\n```py\nvalue \u003d 123\n\ndef test(ab: int):\n    \"\"\"my cool test\n\n    Args:\n        ab: a nice little integer\n    \"\"\"\n    return 0\n\ntest(ab\u003dvalue)\n          ^\n```\n\n````txt\nLiteral[123]\n````\n\nCloses https://github.com/astral-sh/ty/issues/1350.\nCloses https://github.com/astral-sh/ty/issues/3538.\n\n## Test Plan\n\nPlease see included tests.\n\n\u003c!-- How was it tested? --\u003e"
    },
    {
      "commit": "54327092dbfe455040690d63bb1e5e4b5f551239",
      "tree": "04cd1cadba401616e6046c0302a1817140749990",
      "parents": [
        "3cb09eba689ebb49e799131092121928cc789c18"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Wed May 27 18:45:39 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 17:45:39 2026 +0000"
      },
      "message": "Refine a few agents instructions (#25423)"
    },
    {
      "commit": "3cb09eba689ebb49e799131092121928cc789c18",
      "tree": "e1e45731f405bb63b434c4cd961f216123c6d250",
      "parents": [
        "c8cd59f189f2b6f55d542b29bddb953622add6fc"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed May 27 17:15:14 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 16:15:14 2026 +0100"
      },
      "message": "[ty] Support `typing.TypeForm` (#25334)\n\n## Summary\n\n`TypeForm[T]` describes a value that is a valid type expression\nrepresenting a type assignable to `T`. Unlike `type[T]`, it can describe\nforms such as parameterized generics and unions, not only runtime class\nobjects:\n\n```python\nfrom typing import assert_type\nfrom typing_extensions import TypeForm\n\ndef construct[T](form: TypeForm[T]) -\u003e T:\n    raise NotImplementedError\n\nassert_type(construct(int), int)\nassert_type(construct(list[int]), list[int])\nassert_type(construct(int | str), int | str)\n```\n\nThis PR adds support for `TypeForm[T]`, bringing us into alignment with\nthe conformance suite.\n\nFor bare `type`, we treat it as a runtime class value, but one that\ndoesn\u0027t promise a specific represented type, so it\u0027s valid for broad\nTypeForm destinations, but not narrow ones:\n\n```python\ndef use_bare_runtime_class(runtime_type: type) -\u003e None:\n    any_form: TypeForm[Any] \u003d runtime_type\n    object_form: TypeForm[object] \u003d runtime_type\n    string_form: TypeForm[str] \u003d runtime_type  # error\n```\n\nCloses https://github.com/astral-sh/ty/issues/2668."
    },
    {
      "commit": "c8cd59f189f2b6f55d542b29bddb953622add6fc",
      "tree": "aa121e7028525b5b44e2cf51171d75ce27998ddb",
      "parents": [
        "2428fca93267fe46340a850b105dc0d1681b9291"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed May 27 17:02:24 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 16:02:24 2026 +0100"
      },
      "message": "[ty] Infer class attributes assigned by metaclass initialization (#25342)\n\n## Summary\n\nPrior to this change, we didn\u0027t recognize class-object attributes\npopulated by a metaclass during class initialization. For example:\n\n```python\nclass Meta(type):\n    def __init__(cls, name: str, bases: tuple[type, ...], namespace: dict[str, object]) -\u003e None:\n        cls.attr: int \u003d 1\n\nclass C(metaclass\u003dMeta): ...\n\nreveal_type(C.attr)  # revealed: int\n```\n\nWe now recognize attributes that a metaclass adds to a class while\ncreating it. If the metaclass overwrites an existing class-body value,\nwe use the overwritten value\u0027s type. If the class provides an annotation\nfor the generated attribute, we preserve that annotation as its public\ntype.\n\nFor example, if the class body initially gives attr a value, the\nmetaclass assignment happens later at runtime and overwrites it:\n\n```python\nclass Meta(type):\n    def __init__(cls, name, bases, namespace):\n        cls.attr: int \u003d 1\n\nclass C(metaclass\u003dMeta):\n    attr \u003d \"initial value\"\n\nreveal_type(C.attr)  # int\n```\n\nCloses https://github.com/astral-sh/ty/issues/1138."
    },
    {
      "commit": "2428fca93267fe46340a850b105dc0d1681b9291",
      "tree": "66b3c217d94346d338fbe767c9edb584765bcf89",
      "parents": [
        "7c55833109eae24b3a9983e7d75b06463726802e"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed May 27 15:59:49 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 13:59:49 2026 +0000"
      },
      "message": "[ty] Ignore and reject annotations on non-name targets (#25324)\n\n## Summary\n\nLike Mypy and Pyright, we now ignore annotations on non-name (i.e.,\nsubscript and attribute) targets, and emit a diagnostic for the invalid\nannotation -- with the exception of annotated assignments on `self` and\nclass attributes (i.e., receivers).\n\nCloses https://github.com/astral-sh/ty/issues/509."
    },
    {
      "commit": "7c55833109eae24b3a9983e7d75b06463726802e",
      "tree": "cc8c6517cb01b0236ff2f553eb8cdcdcc9c5d654",
      "parents": [
        "7734c74dd070d88046808d5179cecf23a41e41aa"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Wed May 27 05:44:46 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 13:44:46 2026 +0100"
      },
      "message": "Temporarily remove Ibraheem from reviewer pool (#25416)\n\nThis is what we did previously for folks on PTO"
    },
    {
      "commit": "7734c74dd070d88046808d5179cecf23a41e41aa",
      "tree": "2f413d3bcaf21176af12c4955f1629015ad79d3f",
      "parents": [
        "d8607123fb90ecf0176aa8d164a583cc57634186"
      ],
      "author": {
        "name": "David Peter",
        "email": "sharkdp@users.noreply.github.com",
        "time": "Wed May 27 05:40:17 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 12:40:17 2026 +0000"
      },
      "message": "[ty] update flaky projects (#25417)"
    },
    {
      "commit": "d8607123fb90ecf0176aa8d164a583cc57634186",
      "tree": "4dccf808e1c556855f60705e9a45ee4981fb9557",
      "parents": [
        "dfb5e926cbd2e787dca56b925754ccc3f87ed97f"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Wed May 27 14:35:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 13:35:45 2026 +0100"
      },
      "message": "[ty] Reject inconsistent generic bases in dynamic classes (#25413)\n\n## Summary\n\nWe do this for static classes, but were missing the analysis for dynamic\nclasses."
    },
    {
      "commit": "dfb5e926cbd2e787dca56b925754ccc3f87ed97f",
      "tree": "a08e58426b093c528c9dbcf059906cfc5aaff44f",
      "parents": [
        "6aaa91ac2b269df1414954ccd5134f0e6f5c6d30"
      ],
      "author": {
        "name": "Dev-X25874",
        "email": "283057883+Dev-X25874@users.noreply.github.com",
        "time": "Wed May 27 17:57:44 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 12:27:44 2026 +0000"
      },
      "message": "[ty] Add help message to invalid-generic-class variance diagnostic (#25385)\n\n## Summary\n\nWhen a generic subclass uses an explicitly-declared TypeVar in a base\nclass position where the variance is incompatible, the existing\ndiagnostic only reported which type variable and base class were\ninvolved:\n\nVariance of type variable `T_co` is incompatible with base class\n`Invariant`\n\nThis adds a `.help()` sub-diagnostic that explains *why* it is\nincompatible and what the base class actually requires:\n\nType variable `T_co` is declared as covariant, but base class\n`Invariant` requires it to be invariant\n\n\n## Test Plan\n\nAdded a new mdtest section in\n`crates/ty_python_semantic/resources/mdtest/generics/legacy/variance.md`\ncovering all four incompatible variance combinations, verifying the\nexact diagnostic message text.\n\n---------\n\nCo-authored-by: David Peter \u003cmail@david-peter.de\u003e"
    },
    {
      "commit": "6aaa91ac2b269df1414954ccd5134f0e6f5c6d30",
      "tree": "359d65eaea280bd56fc320fe56f8d41225e37b73",
      "parents": [
        "a76571bfbbe28a8e7deb195a1f5f10f6a0355095"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:29:07 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:29:07 2026 +0100"
      },
      "message": "Update dependency ruff to v0.15.14 (#25402)"
    },
    {
      "commit": "a76571bfbbe28a8e7deb195a1f5f10f6a0355095",
      "tree": "d132190ff024e5a26aab262dba43fe7fa2c8b4ba",
      "parents": [
        "60e37a4c7311e6df5ded998dfcd20f04352c6d80"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:27:56 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:27:56 2026 +0100"
      },
      "message": "Update prek dependencies (#25403)"
    },
    {
      "commit": "60e37a4c7311e6df5ded998dfcd20f04352c6d80",
      "tree": "5a74f28c82c36d468b97c746e9fb10cc3da6dfa2",
      "parents": [
        "283652349bdcb299e5bce369629aee103879c126"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:26:43 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:26:43 2026 +0100"
      },
      "message": "Update NPM Development dependencies (#25405)"
    },
    {
      "commit": "283652349bdcb299e5bce369629aee103879c126",
      "tree": "fd8348061898f45bd4ccb619c43c6aa9845fe375",
      "parents": [
        "3b2a1f619e8408ba3b32f6893a9194b21f872fa3"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:26:05 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:26:05 2026 +0100"
      },
      "message": "Update dependency astral-sh/uv to v0.11.16 (#25401)"
    },
    {
      "commit": "3b2a1f619e8408ba3b32f6893a9194b21f872fa3",
      "tree": "f2e39eadf2fd9a3dc00c6850b6821aa8308d3935",
      "parents": [
        "a11c416b9cfa9535613dd188ecbe2dc46a91e924"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:25:53 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:25:53 2026 +0100"
      },
      "message": "Update Rust crate thin-vec to v0.2.18 (#25404)"
    },
    {
      "commit": "a11c416b9cfa9535613dd188ecbe2dc46a91e924",
      "tree": "602584d06e45d1c02c766aff97f951486e765540",
      "parents": [
        "ad5ab668cbb896d211211738ad8d292239256368"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:25:33 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:25:33 2026 +0100"
      },
      "message": "Update Rust crate get-size2 to 0.9.0 (#25407)"
    },
    {
      "commit": "ad5ab668cbb896d211211738ad8d292239256368",
      "tree": "d240e1d4bf7c4d3f0bd7f8729ddea3ad03368867",
      "parents": [
        "b4c3867dcf8d3d36b1177d527eed00e1a17f2ee2"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:24:39 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:24:39 2026 +0100"
      },
      "message": "Update Rust crate dashmap to v6.2.1 (#25406)"
    },
    {
      "commit": "b4c3867dcf8d3d36b1177d527eed00e1a17f2ee2",
      "tree": "dee7ac7cc909608f53c937c9812a643778036d59",
      "parents": [
        "7c2e9fbc83d7e0a87eea99053ec01182258b73a3"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Wed May 27 07:24:10 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 07:24:10 2026 +0100"
      },
      "message": "Update taiki-e/install-action action to v2.79.2 (#25408)"
    },
    {
      "commit": "7c2e9fbc83d7e0a87eea99053ec01182258b73a3",
      "tree": "3ad7e4fc1b55a1002a5048a5316b54ab040854b0",
      "parents": [
        "9c6536e73c10c1aabea72a2b11b218e67c4d090c"
      ],
      "author": {
        "name": "Ruchir",
        "email": "61278001+Ruchir28@users.noreply.github.com",
        "time": "Wed May 27 03:21:25 2026 +0530"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 14:51:25 2026 -0700"
      },
      "message": "[`pyflakes`] Treat function-scope bare annotations as locals per PEP 526 (`F821`) (#21540)\n\n## Summary\n\nResolves #21494 \n\nTreat function-scope annotated variables as locals per PEP 526 ,\npreventing F821 false positives.\n\n## Test Plan\n\nAdded tests for these cases in\n`crates/ruff_linter/resources/test/fixtures/pyflakes/F821_34.py`"
    },
    {
      "commit": "9c6536e73c10c1aabea72a2b11b218e67c4d090c",
      "tree": "570d6e35541f3d392bf2aa714a2cf7a35aeead2a",
      "parents": [
        "0f3ea5adc449e00b805750b5aadf94194eabcfa5"
      ],
      "author": {
        "name": "Brent Westbrook",
        "email": "36778786+ntBre@users.noreply.github.com",
        "time": "Tue May 26 13:54:41 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 13:54:41 2026 -0700"
      },
      "message": "Fix invalid IR when flattening nested `BestFitting` elements (#25398)\n\nSummary\n--\n\nThis PR fixes #25397 by dropping `StartBestFittingEntry` and\n`EndBestFittingEntry` tags in\n`RemoveSoftLinesBuffer`. #25144 changed `RemoveSoftLinesBuffer` to\ninline the most-flat variant of\n`BestFitting` elements, but it inlined them with their start and end\ntags, which caused the problem\nobserved in #25397 when these tags became nested in another\n`BestFitting` element, which interpreted\nthe first `End` tag as ending the outer `BestFitting` rather than the\nnested one. You can see this\nclearly in the IR. First, the diff between the 0.15.13 and 0.15.14 IR,\nwhich reveals the unmatched\nstart tags:\n\n\u003cdetails\u003e\n\u003csummary\u003e0.15.13 vs main\u003c/summary\u003e\n\n```diff\n@@ -38,29 +38,13 @@\n                           \"lambda \",\n                           group([\"arg\"]),\n                           \": \",\n-                          best_fitting([\n-                            [\n-                              [\n-                                \u003cinterned 1\u003e [\n-                                  \"str(\",\n-                                  group([\n-                                    indent([soft_line_break, group([\"arg\"])]),\n-                                    soft_line_break\n-                                  ]),\n-                                  \")\"\n-                                ]\n-                              ]\n-                            ]\n-                            [[group(expand: true, [\u003cref interned *1\u003e])]]\n-                            [\n-                              [\n-                                \"(\",\n-                                indent([hard_line_break, \u003cref interned *1\u003e]),\n-                                hard_line_break,\n-                                \")\"\n-                              ]\n+                          [\n+                            \u003cinterned 1\u003e [\n+                              \"str(\",\n+                              group([indent([group([\"arg\"])])]),\n+                              \")\"\n                             ]\n-                          ]),\n+                          ],\n                           \", self.args\"\n                         ])\n                       ])\n@@ -105,27 +89,9 @@\n                             \"lambda \",\n                             group([\"arg\"]),\n                             \": \",\n-                            best_fitting([\n-                              [\n-                                [\n-                                  \u003cinterned 2\u003e [\n-                                    \"str(\",\n-                                    group([\n-                                      indent([soft_line_break, group([\"arg\"])]),\n-                                      soft_line_break\n-                                    ]),\n-                                    \")\"\n-                                  ]\n-                                ]\n-                              ]\n-                              [[group(expand: true, [\u003cref interned *2\u003e])]]\n-                              [\n-                                [\n-                                  \"(\",\n-                                  indent([hard_line_break, \u003cref interned *2\u003e]),\n-                                  hard_line_break,\n-                                  \")\"\n-                                ]\n+                            [\n+                              \u003cinterned 2\u003e [\n+                                \"str(\",\n+                                group([indent([group([\"arg\"])])]),\n+                                \")\"\n                               ]\n-                            ]),\n-                            \", self.args\"\n+                            ]\n                           ])\n+                          \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n                         ])\n-                      ]),\n-                      \")\"\n+                        \u003cSTART_WITHOUT_END\u003cIndent\u003e\u003e\n+                      ])\n+                      \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n                     ])\n+                    \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n                   ])\n-                ]),\n-                \")})\\\"\"\n-              ]),\n-              hard_line_break,\n-              \")\"\n+                  \u003cSTART_WITHOUT_END\u003cIndent\u003e\u003e\n+                ])\n+                \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n+              ])\n+              \u003cSTART_WITHOUT_END\u003cIndent\u003e\u003e\n             ])\n-          ]\n+            \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n+          ])\n+          \u003cSTART_WITHOUT_END\u003cBestFittingEntry\u003e\u003e\n         ]\n       ])\n     ]),\n```\n\n\u003c/details\u003e\n\nand then the diff between main and this PR, which shows the removed\nbrackets that represent the\nnested tags:\n\n\u003cdetails\u003e\n\u003csummary\u003emain vs PR\u003c/summary\u003e\n\n```diff\n@@ -38,12 +38,10 @@\n                           \"lambda \",\n                           group([\"arg\"]),\n                           \": \",\n-                          [\n-                            \u003cinterned 1\u003e [\n-                              \"str(\",\n-                              group([indent([group([\"arg\"])])]),\n-                              \")\"\n-                            ]\n+                          \u003cinterned 1\u003e [\n+                            \"str(\",\n+                            group([indent([group([\"arg\"])])]),\n+                            \")\"\n                           ],\n                           \", self.args\"\n                         ])\n@@ -89,24 +87,15 @@\n                             \"lambda \",\n                             group([\"arg\"]),\n                             \": \",\n-                            [\n-                              \u003cinterned 2\u003e [\n-                                \"str(\",\n-                                group([indent([group([\"arg\"])])]),\n-                                \")\"\n-                              ]\n-                            ]\n+                            \u003cinterned 2\u003e [\n+                              \"str(\",\n+                              group([indent([group([\"arg\"])])]),\n+                              \")\"\n+                            ],\n+                            \", self.args\"\n                           ])\n-                          \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n                         ])\n-                        \u003cSTART_WITHOUT_END\u003cIndent\u003e\u003e\n-                      ])\n-                      \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n+                      ]),\n+                      \")\"\n                     ])\n-                    \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n                   ])\n-                  \u003cSTART_WITHOUT_END\u003cIndent\u003e\u003e\n-                ])\n-                \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n-              ])\n-              \u003cSTART_WITHOUT_END\u003cIndent\u003e\u003e\n+                ]),\n+                \")})\\\"\"\n+              ]),\n+              hard_line_break,\n+              \")\"\n             ])\n-            \u003cSTART_WITHOUT_END\u003cGroup\u003e\u003e\n-          ])\n-          \u003cSTART_WITHOUT_END\u003cBestFittingEntry\u003e\u003e\n+          ]\n         ]\n       ])\n     ]),\n```\n\n\u003c/details\u003e\n\nI think the diff between 0.15.13 and this PR is also nice to see because\nit makes it a bit more\nclear that the `BestFitting` element has been inlined:\n\n\u003cdetails\u003e\n\u003csummary\u003e0.15.13 vs this PR\u003c/summary\u003e\n\n```diff\n@@ -38,29 +38,11 @@\n                           \"lambda \",\n                           group([\"arg\"]),\n                           \": \",\n-                          best_fitting([\n-                            [\n-                              [\n-                                \u003cinterned 1\u003e [\n-                                  \"str(\",\n-                                  group([\n-                                    indent([soft_line_break, group([\"arg\"])]),\n-                                    soft_line_break\n-                                  ]),\n-                                  \")\"\n-                                ]\n-                              ]\n-                            ]\n-                            [[group(expand: true, [\u003cref interned *1\u003e])]]\n-                            [\n-                              [\n-                                \"(\",\n-                                indent([hard_line_break, \u003cref interned *1\u003e]),\n-                                hard_line_break,\n-                                \")\"\n-                              ]\n-                            ]\n-                          ]),\n+                          \u003cinterned 1\u003e [\n+                            \"str(\",\n+                            group([indent([group([\"arg\"])])]),\n+                            \")\"\n+                          ],\n                           \", self.args\"\n                         ])\n                       ])\n@@ -105,29 +87,11 @@\n                             \"lambda \",\n                             group([\"arg\"]),\n                             \": \",\n-                            best_fitting([\n-                              [\n-                                [\n-                                  \u003cinterned 2\u003e [\n-                                    \"str(\",\n-                                    group([\n-                                      indent([soft_line_break, group([\"arg\"])]),\n-                                      soft_line_break\n-                                    ]),\n-                                    \")\"\n-                                  ]\n-                                ]\n-                              ]\n-                              [[group(expand: true, [\u003cref interned *2\u003e])]]\n-                              [\n-                                [\n-                                  \"(\",\n-                                  indent([hard_line_break, \u003cref interned *2\u003e]),\n-                                  hard_line_break,\n-                                  \")\"\n-                                ]\n-                              ]\n-                            ]),\n+                            \u003cinterned 2\u003e [\n+                              \"str(\",\n+                              group([indent([group([\"arg\"])])]),\n+                              \")\"\n+                            ],\n                             \", self.args\"\n                           ])\n                         ])\n@@ -155,7 +119,6 @@\n\n     def __str__(self) -\u003e str:\n         return (\n-            f\"first stringlist of args long\u003d{list(map(lambda arg: str(\n-                        arg\n-                    ), self.args))})\"\n+            \"first string\"\n+            f\"list of args long\u003d{list(map(lambda arg: str(arg), self.args))})\"\n         )\n```\n\n\u003c/details\u003e\n\nAnd you can also see at the bottom here that the actual formatting that\n#25144 sought to resolve\nis still fixed.\n\nTest Plan\n--\n\nNew regression test based on the code in the issue"
    },
    {
      "commit": "0f3ea5adc449e00b805750b5aadf94194eabcfa5",
      "tree": "e2df69f284efd09ebc0c89ad564f8510c40ef1ee",
      "parents": [
        "38562889b9dfea7b15ce3e36670667dc395d063e"
      ],
      "author": {
        "name": "Douglas Creager",
        "email": "dcreager@dcreager.net",
        "time": "Tue May 26 15:56:28 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 15:56:28 2026 -0400"
      },
      "message": "[ty] Use constraint sets as pending state in `SpecializationBuilder` (#24540)\n\nThis PR implements the bulk of the logic needed for\nhttps://github.com/astral-sh/ty/issues/2799, by building up a\n`ConstraintSet` in `SpecializationBuilder`. This constraint set holds\nthe \"pending state\" of the specialization being inferred. It will\neventually _replace_ the `types` hash map, which stores a simple mapping\nfrom typevars to inferred types, and which is always updated by unioning\ntogether types when we find multiple solutions for a typevar.\n\nFor now, we are building up both pending state representations, and\nsolving from the constraint set where we can. There are two main cases\nwhere we must fall back on the old solver:\n\n- Any specialization that involves a `ParamSpec`\n- If the constraint set returns `Unsatisfiable` or `Unconstrained`\n\nFollow-on PRs will address these remaining cases that use the old\nsolver; once they are all migrated over to the constraint set solver, we\nwill remove the `types` field.\n\nCloses astral-sh/ty#2572\nCloses astral-sh/ty#2959\nCloses astral-sh/ty#3037\nCloses astral-sh/ty#3203\nCloses astral-sh/ty#3228\nCloses astral-sh/ty#3428\nCloses astral-sh/ty#3483"
    },
    {
      "commit": "38562889b9dfea7b15ce3e36670667dc395d063e",
      "tree": "f7512646a6c903eff90874394a57c265019fd3ad",
      "parents": [
        "258ca11050a1b4dca1cc2ed8698261333404b866"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Tue May 26 18:08:12 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 18:08:12 2026 +0100"
      },
      "message": "Reduce memory usage by dropping token-excess capacity and improve performance by approximating the initial tokens vec size (#25354)\n\nCo-authored-by: Dhruv Manilawala \u003cdhruvmanila@gmail.com\u003e"
    },
    {
      "commit": "258ca11050a1b4dca1cc2ed8698261333404b866",
      "tree": "90c0c8a211831e8fcee8be038383f9d40ef725d2",
      "parents": [
        "305808d9ade492a60aebb49067e2a04fa999d61d"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Tue May 26 17:16:37 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 16:16:37 2026 +0100"
      },
      "message": "[ty] Remove Prefect from flaky project list (#25394)\n\n## Summary\n\nInterestingly, it appears that this was \"fixed\" by\nhttps://github.com/astral-sh/ruff/pull/24927 based on a local basic,\nwhich suggest that the source of the non-determinism is in the\ninferred-variance computation, and we now avoid that in Prefect."
    },
    {
      "commit": "305808d9ade492a60aebb49067e2a04fa999d61d",
      "tree": "d604641e6f1f4147e9d1eb3e377a937919bce69b",
      "parents": [
        "f8592700d0862fdc48942199087ad091a2fb68af"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Tue May 26 15:58:14 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 15:58:14 2026 +0100"
      },
      "message": "Update `configuration.md` (#25396)"
    },
    {
      "commit": "f8592700d0862fdc48942199087ad091a2fb68af",
      "tree": "3b3d9746f7e9e2146926bff060f02b0b68c06122",
      "parents": [
        "f2fb134161ed5842388b46e65de4435117601880"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Tue May 26 15:55:24 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 15:55:24 2026 +0100"
      },
      "message": "[ty] Treat Python notebook text documents as Python source (#25393)"
    },
    {
      "commit": "f2fb134161ed5842388b46e65de4435117601880",
      "tree": "9b374e643333fe3295765f5e743ecce53347278c",
      "parents": [
        "c0d692ef2af2e7fc20c62c189b34b1a0d0d20596"
      ],
      "author": {
        "name": "Martin Schlossarek",
        "email": "33715215+martin-schlossarek@users.noreply.github.com",
        "time": "Tue May 26 15:38:36 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 13:38:36 2026 +0000"
      },
      "message": "Fix line-length example for --config option (#25389)\n\nCo-authored-by: Martin Schlossarek \u003cmartin@schlossarek.me\u003e"
    },
    {
      "commit": "c0d692ef2af2e7fc20c62c189b34b1a0d0d20596",
      "tree": "6b7b0119b2aa75b389ad235adebc355cdde3a33c",
      "parents": [
        "46f49936aba316c397d9fccab3d96d8b0f2bb3c6"
      ],
      "author": {
        "name": "Alex Waygood",
        "email": "Alex.Waygood@Gmail.com",
        "time": "Tue May 26 14:08:52 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 13:08:52 2026 +0000"
      },
      "message": "[ty] Add dd-trace-py to flaky.txt (#25391)\n\nUnfortunately this project is now flaky following\nhttps://github.com/astral-sh/ruff/commit/95eec58af266ba0f98bb923cf204f442523e813d.\nAdding it to flaky.txt means we don\u0027t get spurious/confusing diffs like\nhttps://github.com/astral-sh/ruff/pull/25017#issuecomment-4390844917\nanymore, because ecosystem-analyzer will run the project several times\nto ensure that newly reported diagnostic diffs are not flaky. The cost\nis that ecosystem-analyzer will take a little longer in CI"
    },
    {
      "commit": "46f49936aba316c397d9fccab3d96d8b0f2bb3c6",
      "tree": "c53378fc462193a19cdd94e557036385d89180f3",
      "parents": [
        "6a3bc5b6bab7af992fa02638b8623cb0edd58e13"
      ],
      "author": {
        "name": "Minh Vu",
        "email": "vuhoangminh97@gmail.com",
        "time": "Tue May 26 10:32:16 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 08:32:16 2026 +0000"
      },
      "message": "Return code action for `codeAction/resolve` requests that contain no or no valid URL (#25365)\n\nCo-authored-by: Micha Reiser \u003cmicha@reiser.io\u003e"
    },
    {
      "commit": "6a3bc5b6bab7af992fa02638b8623cb0edd58e13",
      "tree": "66d94db19c5d0fc5f9e42a7eefeea387400c5dbe",
      "parents": [
        "73de9b45cf98c5003918edcee658301fcc3bf723"
      ],
      "author": {
        "name": "Micha Reiser",
        "email": "micha@reiser.io",
        "time": "Tue May 26 08:34:54 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 07:34:54 2026 +0000"
      },
      "message": "[ty] Show `LiteralString` when hovering the inline of a literal string (#25373)\n\n## Summary\n\nToday, ty showed the hover for `str` when hovering the inlay of a\n`LiteralString` type:\n\n```py\nfrom typing import LiteralString\ndef my_func(x: LiteralString):\n    y[: LiteralString] \u003d x\nmy_func(x\u003d\"hello\")\n```\n\nHovering the inlay of `y` (`[: LiteralString]` is the inlay) showed the\nhover of `str`.\n\nWe argued in https://github.com/astral-sh/ruff/pull/21548 that `str` is\ngenerally the more useful type in this position. I sort of agree, but I\nthink it\u0027s more important that the inlay and the hover show the same\ntype. I also think that it\u0027s important that the hover is consistent with\nwhen we \"bake\" the inlay, e.g. hovering `LiteralString` now also shows\n`LiteralString` and not `str`\n\n```py\nfrom typing import LiteralString\ndef my_func(x: LiteralString):\n    y: LiteralString \u003d x\nmy_func(x\u003d\"hello\")\n```\n\nCloses https://github.com/astral-sh/ty/issues/2860\n\n## Test Plan\n\nUpdated test"
    },
    {
      "commit": "73de9b45cf98c5003918edcee658301fcc3bf723",
      "tree": "6baa3213f63e57dfa157c3933afc76e138ea2294",
      "parents": [
        "26e3516d93c06c7df936cc4356717af7ba621529"
      ],
      "author": {
        "name": "Anuraag (Rag) Agrawal",
        "email": "anuraaga@gmail.com",
        "time": "Tue May 26 01:29:57 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 25 17:29:57 2026 +0100"
      },
      "message": "[ty] Resolve enum names for all \u0027|\u0027 arms for literal enum subsets (#25379)\n\n## Summary\n\nFixes https://github.com/astral-sh/ty/issues/3531\n\nCurrently, only a single enum value is retrieved when evaluating a match\nstatement against a literal of enum values. This recursively resolves a\nfull `|` union statement to get all the values.\n\n## Test Plan\n\n\u003c!-- How was it tested? --\u003e\n\n- Unit tests\n- Built ty and checked against repro in linked issue\n\n---------\n\nCo-authored-by: Charlie Marsh \u003ccharlie.r.marsh@gmail.com\u003e"
    },
    {
      "commit": "26e3516d93c06c7df936cc4356717af7ba621529",
      "tree": "89eb9b62335a8ad78e846bed82b39ba858471318",
      "parents": [
        "58b5d883d1959024ce96354365b004e218d23052"
      ],
      "author": {
        "name": "Charlie Marsh",
        "email": "charlie.r.marsh@gmail.com",
        "time": "Mon May 25 17:17:06 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 25 16:17:06 2026 +0100"
      },
      "message": "[ty] Accept complete enum-literal alias unions as enums (#25341)\n\n## Summary\n\nRelation checking needs to normalize PEP 695 unions together, so that we\ncan recognize that (e.g.) a union of aliases covering every member of an\nenum is equal to the enum itself.\n\nCloses https://github.com/astral-sh/ty/issues/3446."
    },
    {
      "commit": "58b5d883d1959024ce96354365b004e218d23052",
      "tree": "75a8935ac065db96504857400a89e70a062ae90f",
      "parents": [
        "6ab8b737c5407bc1896ce97e56dd168b918a83f0"
      ],
      "author": {
        "name": "renovate[bot]",
        "email": "29139614+renovate[bot]@users.noreply.github.com",
        "time": "Sun May 24 16:45:08 2026 +0100"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun May 24 16:45:08 2026 +0100"
      },
      "message": "Update Rust crate thin-vec to v0.2.16 (#25372)"
    }
  ],
  "next": "6ab8b737c5407bc1896ce97e56dd168b918a83f0"
}
