Skip to content

[ty] Fix TODO for meta-type of intersections#24761

Draft
AlexWaygood wants to merge 1 commit into
mainfrom
alex/inter-todo
Draft

[ty] Fix TODO for meta-type of intersections#24761
AlexWaygood wants to merge 1 commit into
mainfrom
alex/inter-todo

Conversation

@AlexWaygood

Copy link
Copy Markdown
Member

Summary

Test Plan

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Apr 21, 2026
@astral-sh-bot

astral-sh-bot Bot commented Apr 21, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 91.94%. The percentage of expected errors that received a diagnostic held steady at 87.09%. The number of fully passing files held steady at 92/134.

@astral-sh-bot

astral-sh-bot Bot commented Apr 21, 2026

Copy link
Copy Markdown

Memory usage report

Summary

Project Old New Diff Outcome
sphinx 267.51MB 267.54MB +0.01% (30.33kB)
prefect 722.28MB 722.31MB +0.00% (27.84kB)
trio 113.80MB 113.81MB +0.00% (5.18kB)
flake8 46.81MB 46.81MB +0.00% (12.00B)

Significant changes

Click to expand detailed breakdown

sphinx

Name Old New Diff Outcome
Type<'db>::member_lookup_with_policy_ 7.28MB 7.28MB +0.06% (4.73kB)
IntersectionType 533.04kB 536.47kB +0.64% (3.43kB)
is_redundant_with_impl 992.10kB 995.14kB +0.31% (3.05kB)
is_redundant_with_impl::interned_arguments 1.16MB 1.16MB +0.25% (3.01kB)
try_call_bin_op_return_type_impl 242.74kB 245.46kB +1.12% (2.72kB)
check_file_impl 4.51MB 4.51MB +0.05% (2.20kB)
infer_expression_types_impl 21.98MB 21.98MB +0.01% (2.18kB)
Type<'db>::class_member_with_policy_ 7.94MB 7.94MB +0.03% (2.14kB)
Type<'db>::try_call_dunder_get_ 5.11MB 5.12MB +0.02% (1.10kB)
infer_scope_types_impl 13.47MB 13.47MB +0.01% (1.05kB)
Type<'db>::member_lookup_with_policy_::interned_arguments 2.73MB 2.73MB +0.04% (1.02kB)
Type<'db>::try_call_dunder_get_::interned_arguments 1.21MB 1.21MB +0.07% (832.00B)
lookup_dunder_new_inner 116.93kB 117.47kB +0.46% (556.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 2.39MB 2.39MB +0.02% (448.00B)
Type<'db>::class_member_with_policy_::interned_arguments 4.21MB 4.21MB +0.01% (416.00B)
... 9 more

prefect

Name Old New Diff Outcome
Type<'db>::member_lookup_with_policy_ 17.13MB 17.14MB +0.05% (8.03kB)
IntersectionType 897.55kB 903.41kB +0.65% (5.86kB)
is_redundant_with_impl::interned_arguments 2.36MB 2.36MB +0.14% (3.35kB)
is_redundant_with_impl 2.16MB 2.16MB +0.15% (3.22kB)
infer_scope_types_impl 50.85MB 50.85MB +0.00% (2.30kB)
Type<'db>::class_member_with_policy_ 18.29MB 18.29MB +0.01% (2.28kB)
try_call_bin_op_return_type_impl 350.75kB 352.28kB +0.44% (1.54kB)
Type<'db>::member_lookup_with_policy_::interned_arguments 6.34MB 6.34MB +0.02% (1.32kB)
Type<'db>::try_call_dunder_get_ 11.52MB 11.52MB +0.01% (884.00B)
StaticClassLiteral<'db>::try_mro_ 5.34MB 5.34MB -0.01% (840.00B)
CallableType 2.79MB 2.79MB -0.02% (608.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_ 7.02MB 7.03MB +0.01% (560.00B)
Type<'db>::try_call_dunder_get_::interned_arguments 3.15MB 3.16MB +0.02% (520.00B)
StaticClassLiteral<'db>::implicit_attribute_inner_::interned_arguments 5.70MB 5.70MB +0.01% (480.00B)
Specialization 2.63MB 2.63MB -0.02% (464.00B)
... 13 more

trio

Name Old New Diff Outcome
IntersectionType 130.02kB 131.69kB +1.28% (1.66kB)
Type<'db>::try_call_dunder_get_ 1.31MB 1.31MB +0.06% (808.00B)
infer_scope_types_impl 4.14MB 4.14MB +0.02% (792.00B)
lookup_dunder_new_inner 54.21kB 54.87kB +1.23% (680.00B)
Type<'db>::member_lookup_with_policy_ 1.93MB 1.94MB +0.03% (672.00B)
is_redundant_with_impl::interned_arguments 222.15kB 222.75kB +0.27% (616.00B)
infer_expression_type_impl 1.36MB 1.36MB -0.04% (600.00B)
is_redundant_with_impl 194.95kB 195.46kB +0.26% (516.00B)
all_negative_narrowing_constraints_for_expression 591.75kB 591.28kB -0.08% (480.00B)
all_narrowing_constraints_for_expression 612.65kB 612.18kB -0.08% (480.00B)
infer_expression_types_impl 6.75MB 6.75MB -0.01% (408.00B)
infer_definition_types 7.69MB 7.69MB -0.00% (396.00B)
Type<'db>::class_member_with_policy_ 2.00MB 2.00MB +0.02% (388.00B)
Type<'db>::member_lookup_with_policy_::interned_arguments 897.71kB 898.02kB +0.03% (312.00B)
TupleType 107.72kB 107.97kB +0.23% (256.00B)
... 8 more

flake8

Name Old New Diff Outcome
try_call_bin_op_return_type_impl 7.65kB 7.66kB +0.15% (12.00B)

@astral-sh-bot

astral-sh-bot Bot commented Apr 21, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-argument-type 7 2 7
unused-type-ignore-comment 10 3 0
unknown-argument 12 0 0
too-many-positional-arguments 11 0 0
invalid-return-type 3 1 0
invalid-assignment 1 0 0
missing-argument 1 0 0
no-matching-overload 1 0 0
not-iterable 0 1 0
possibly-missing-attribute 1 0 0
Total 47 7 7
Raw diff (61 changes)
bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/embed/standalone.py:287:28 error[too-many-positional-arguments] Too many positional arguments to `Model.__init__`: expected 1, got 2
+ src/bokeh/embed/standalone.py:287:28 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2

core (https://github.com/home-assistant/core)
- homeassistant/runner.py:234:13 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `Mapping[str, object] | None`, found `tuple[type[@Todo], Any, Any]`
+ homeassistant/runner.py:234:13 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `Mapping[str, object] | None`, found `tuple[type[Unknown], Any, Any]`
- homeassistant/runner.py:234:13 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `bool`, found `tuple[type[@Todo], Any, Any]`
+ homeassistant/runner.py:234:13 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `bool`, found `tuple[type[Unknown], Any, Any]`
- homeassistant/runner.py:234:13 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `int`, found `tuple[type[@Todo], Any, Any]`
+ homeassistant/runner.py:234:13 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `int`, found `tuple[type[Unknown], Any, Any]`
- homeassistant/runner.py:242:9 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `Mapping[str, object] | None`, found `tuple[type[@Todo], Any, Any]`
+ homeassistant/runner.py:242:9 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `Mapping[str, object] | None`, found `tuple[type[Unknown], Any, Any]`
- homeassistant/runner.py:242:9 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `bool`, found `tuple[type[@Todo], Any, Any]`
+ homeassistant/runner.py:242:9 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `bool`, found `tuple[type[Unknown], Any, Any]`
- homeassistant/runner.py:242:9 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `int`, found `tuple[type[@Todo], Any, Any]`
+ homeassistant/runner.py:242:9 error[invalid-argument-type] Argument to bound method `Logger.error` is incorrect: Expected `int`, found `tuple[type[Unknown], Any, Any]`

discord.py (https://github.com/Rapptz/discord.py)
+ discord/ext/commands/core.py:1183:16 warning[possibly-missing-attribute] Attribute `__cog_name__` may be missing on object of type `type[CogT@Command] & ~<class 'NoneType'>`
+ discord/ui/view.py:952:17 error[invalid-assignment] Invalid subscript assignment with key of type `Pattern[str]` and value of type `type[Item[Any]] & type[DynamicItem[object]]` on object of type `dict[Pattern[str], type[DynamicItem[Item[Any]]]]`

graphql-core (https://github.com/graphql-python/graphql-core)
+ src/graphql/language/visitor.py:229:43 error[invalid-argument-type] Argument is incorrect: Expected `Location | None`, found `Any | Node`
+ src/graphql/language/visitor.py:229:43 error[invalid-argument-type] Argument is incorrect: Expected `Location | None`, found `Any | Node`
+ src/graphql/language/visitor.py:229:43 error[invalid-argument-type] Argument is incorrect: Expected `Location | None`, found `Any | Node`

koda-validate (https://github.com/keithasaurus/koda-validate)
+ koda_validate/namedtuple.py:130:43 error[invalid-argument-type] Argument to function `signature` is incorrect: Expected `(...) -> Any`, found `type[_NTT@NamedTupleValidator]`

mitmproxy (https://github.com/mitmproxy/mitmproxy)
- test/mitmproxy/proxy/tutils.py:354:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[Command, type[CommandCompleted]].__getitem__(key: Command, /) -> type[CommandCompleted]` cannot be called with key of type `type[@Todo]` on object of type `dict[Command, type[CommandCompleted]]`
+ test/mitmproxy/proxy/tutils.py:354:25 error[invalid-argument-type] Method `__getitem__` of type `bound method dict[Command, type[CommandCompleted]].__getitem__(key: Command, /) -> type[CommandCompleted]` cannot be called with key of type `type[Command]` on object of type `dict[Command, type[CommandCompleted]]`

operator (https://github.com/canonical/operator)
- ops/model.py:1908:69 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/dtypes/concat.py:126:17 error[unknown-argument] Argument `axis` does not match any known parameter of bound method `ExtensionArray._concat_same_type`
+ pandas/core/sorting.py:629:37 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2

psycopg (https://github.com/psycopg/psycopg)
+ psycopg/psycopg/rows.py:137:16 error[invalid-return-type] Return type does not match returned value: expected `RowMaker[tuple[object, ...] & NamedTupleLike]`, found `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike`
+ psycopg/psycopg/rows.py:145:12 error[invalid-return-type] Return type does not match returned value: expected `type[tuple[object, ...]] & type[NamedTupleLike]`, found `<class 'Row'>`
+ psycopg/psycopg/types/composite.py:514:12 error[invalid-return-type] Return type does not match returned value: expected `type[tuple[object, ...]] & type[NamedTupleLike]`, found `<class '<unknown>'>`

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/tasks/historical_balances.py:592:36 error[missing-argument] No argument provided for required parameter `group_identifier` of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:593:17 error[unknown-argument] Argument `tx_ref` does not match any known parameter of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:593:17 error[invalid-argument-type] Argument to `OnchainEvent.__init__` is incorrect: Expected `Never`, found `object`
+ rotkehlchen/tasks/historical_balances.py:603:17 error[unknown-argument] Argument `counterparty` does not match any known parameter of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:604:17 error[unknown-argument] Argument `address` does not match any known parameter of `HistoryBaseEntry.__init__`
+ rotkehlchen/tasks/historical_balances.py:604:17 error[invalid-argument-type] Argument to `OnchainEvent.__init__` is incorrect: Expected `None`, found `object`

scipy (https://github.com/scipy/scipy)
+ scipy/sparse/_index.py:54:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:54:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:54:50 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:54:50 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:54:67 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:54:67 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:56:43 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ scipy/sparse/_index.py:56:52 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:52 error[unknown-argument] Argument `shape` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:69 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`
+ scipy/sparse/_index.py:56:69 error[unknown-argument] Argument `dtype` does not match any known parameter of `object.__init__`

scrapy (https://github.com/scrapy/scrapy)
+ scrapy/utils/python.py:271:27 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2

sphinx (https://github.com/sphinx-doc/sphinx)
+ sphinx/config.py:708:45 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ sphinx/config.py:739:41 error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
+ sphinx/domains/std/__init__.py:1392:26 error[no-matching-overload] No overload of bound method `dict.get` matches arguments

static-frame (https://github.com/static-frame/static-frame)
- static_frame/core/index_hierarchy.py:2173:20 error[invalid-return-type] Return type does not match returned value: expected `int | integer[Any] | ndarray[Any, Any] | ... omitted 3 union elements`, found `list[int | integer[Any] | ndarray[Any, Any] | ... omitted 3 union elements]`
- static_frame/core/index_hierarchy.py:2173:51 error[not-iterable] Object of type `ndarray[tuple[object, ...], dtype[object]] & ~int & ~Top[integer[Any]] & ~IndexHierarchy & ~Top[list[Unknown]]` is not iterable
+ static_frame/core/index_hierarchy.py:2160:75 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:187:32 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:190:69 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:191:61 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:199:41 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:202:68 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:204:60 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:207:88 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:209:55 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
+ static_frame/core/loc_map.py:210:40 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/quilt.py:856:58 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/quilt.py:862:46 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/type_blocks.py:3360:39 error[invalid-argument-type] Method `__getitem__` of type `Overload[(key: ndarray[tuple[Any, ...], dtype[integer[Any] | numpy.bool[builtins.bool]]] | tuple[ndarray[tuple[Any, ...], dtype[integer[Any] | numpy.bool[builtins.bool]]], ...], /) -> ndarray[tuple[Any, ...], dtype[object]], (key: SupportsIndex | tuple[SupportsIndex, ...], /) -> Any, (key: SupportsIndex | slice[Any, Any, Any] | EllipsisType | ... omitted 5 union elements, /) -> ndarray[tuple[Any, ...], dtype[object]], (key: str, /) -> ndarray[tuple[object, ...], dtype[Any]], (key: list[str], /) -> ndarray[tuple[object, ...], Unknown]]` cannot be called with key of type `slice[Any, Any, Any]` on object of type `ndarray[tuple[object, ...], dtype[object]]`
- static_frame/core/type_blocks.py:3370:21 error[invalid-argument-type] Method `__getitem__` of type `Overload[(key: ndarray[tuple[Any, ...], dtype[integer[Any] | numpy.bool[builtins.bool]]] | tuple[ndarray[tuple[Any, ...], dtype[integer[Any] | numpy.bool[builtins.bool]]], ...], /) -> ndarray[tuple[Any, ...], dtype[object]], (key: SupportsIndex | tuple[SupportsIndex, ...], /) -> Any, (key: SupportsIndex | slice[Any, Any, Any] | EllipsisType | ... omitted 5 union elements, /) -> ndarray[tuple[Any, ...], dtype[object]], (key: str, /) -> ndarray[tuple[object, ...], dtype[Any]], (key: list[str], /) -> ndarray[tuple[object, ...], Unknown]]` cannot be called with key of type `tuple[slice[Any, Any, Any], slice[Any, Any, Any]]` on object of type `ndarray[tuple[object, ...], dtype[object]]`
+ static_frame/test/unit/test_quilt.py:2111:45 error[invalid-argument-type] Argument to bound method `Quilt._axis_tuple` is incorrect: Expected `(type[tuple[object, ...]] & type[NamedTupleLike]) | None`, found `<class 'tuple'>`

sympy (https://github.com/sympy/sympy)
+ sympy/physics/quantum/spin.py:1028:21 error[too-many-positional-arguments] Too many positional arguments to constructor `SpinState.__new__`: expected 3, got 4

Full report with detailed diff (timing results)

@AlexWaygood

AlexWaygood commented Apr 21, 2026

Copy link
Copy Markdown
Member Author

Ecosystem analysis (WIP)

Here's a minimized version of the bokeh hits

Details
from typing import Any, Sequence

class Model: ...

def f(
    models: Model | dict[str, Model],
    x: dict[object, object],
    y: list[str],
):
    dict_type: type[dict[Any, Any]] = dict
    
    if isinstance(models, dict):
        # (Model & Top[dict[Unknown, Unknown]]) | dict[str, Model]
        reveal_type(models)
        # (type[Model] & type[Top[dict[Unknown, Unknown]]]) | type[dict[str, Model]]
        reveal_type(models.__class__)
        dict_type = models.__class__
    
    # <class 'dict'> | (type[Model] & type[Top[dict[Unknown, Unknown]]]) | type[dict[str, Model]]
    reveal_type(dict_type)

    # error[no-matching-overload] No overload of `dict.__init__` matches arguments
    # error[no-matching-overload] No overload of `dict.__init__` matches arguments
    # error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2
    dict_type(zip(x.keys(), y))

On main, we emit the first diagnostic on the last line there, but not the second or the third.

This feels like yet another instance where our narrowing-via-intersections behaviour is strictly correct, but will likely be quite confusing for our users. Still, this behaviour is consistent with our behaviour elsewhere, so it's not really a bug in this PR.


A minimized version of the koda-validate hit is this:

from typing import NamedTuple, Callable, Any

def f[T: NamedTuple](x: type[T], y: NamedTuple) -> type[T]:
    reveal_type(x)  # type[T@f]
    reveal_type(y)  # tuple[object, ...] & NamedTupleLike
    # error[invalid-assignment] Object of type `type[T@f]` is not assignable to `(...) -> Any`
    z: Callable[..., Any] = x
    return x

This is due to a bug in our intersection <-> Callable type relations. I tried to fix it in #24830, but it's nontrivial; I don't think fixing it should block this PR. An issue tracking it is astral-sh/ty#3612.


The pandas hits look similar to the bokeh hits, and they already have type: ignore[call-arg] commments on those lines for mypy.


The psycopg hits minimize to this:

from typing import NamedTuple, Any, Sequence, Protocol

class RowMaker[Row: tuple[Any, ...]](Protocol):
    def __call__(self, values: Sequence[Any], /) -> Row: ...

def namedtuple_row(namedtuple_cls: type[NamedTuple]) -> RowMaker[NamedTuple]:
    # error[invalid-return-type] Return type does not match returned value: expected `RowMaker[tuple[object, ...] & NamedTupleLike]`, found `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike`
    return namedtuple_cls._make

If this PR is rebased on #24770, we get

info: type `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike` is not assignable to protocol `RowMaker[tuple[object, ...] & NamedTupleLike]`
info: └── protocol member `__call__` is incompatible
info:     └── incompatible return types: `NamedTupleLike` is not assignable to `tuple[object, ...] & NamedTupleLike`

And a minimal repro of this that demonstrates the issue on main is:

from ty_extensions import Intersection
from typing import Self, reveal_type

class Bar:
    def method(self) -> Self:
        return self

class Foo: ...

def f(x: Intersection[Bar, Foo]):
    reveal_type(x.method())

I opened astral-sh/ty#3565 to track this.


The many diagnostics going away on static-frame minimize to this:

from typing import Hashable

def needs_slice(k: slice) -> None: ...                                                                                                          

def f(key: Hashable) -> None:
    if isinstance(key, int):
        return
    if key.__class__ is slice:
        needs_slice(key)

The diagnostics go away because key.__class__ is now inferred as type[Hashable] & ~type[int], and everything under the if key.__class__ is slice condition is inferred as unreachable, so key in that branch is inferred as Never. This isn't strictly a false negative, since perhaps ideally we'd understand if foo.__class__ is Bar conditions properly, in which case we'd narrow the type of key here and would have the same user-facing behaviour. We're getting the right answer for the wrong reasons on this branch, though: here's a minimal repro that reproduces on main:

from typing import Hashable
                                                                                                                                                
                                                                                                                                                
def f(x: Hashable):
    y = type(x)
    if y is slice:
        reveal_type(x)

AlexWaygood added a commit that referenced this pull request May 29, 2026
## Summary

A bunch of followup changes from
#25422. Mostly triggered by using
the skill for the first time to analyze the report in
#24761.

- Move ty-specific guidance from `AGENTS.md` to a new local
`working-on-ty` skill. Link to the other ty-specific skills from that
skill.
- Add more keyword triggers to various skills so that agents are more
likely to automatically figure out that a skill is appropriate
(suggested by @dhruvmanila).
- Add more guidance to the ecosystem-minimization skill (and tweak the
script for setting up a primer project) so that agents are likely to
reproduce results in a deterministic and helpful way.

## Test Plan

I repeatedly ran `/review` with Codex on this until the only remaining
issues were trivial.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant